Skip to main content
@rhtml Logo

Build Modern Web Apps with @rhtml

The reactive, declarative framework for building scalable web applications. Combine the power of Web Components, RxJS, and dependency injection in one elegant solution.

See @rhtml in Action

Experience the power of functional reactive components

app.component.ts
import { Component, DefineDependencies } from '@rhtml/component';
import { Container, Injectable } from '@rxdi/core';
import { html, LitElement, property } from '@rxdi/lit-html';
import { BehaviorSubject } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
class CounterService {
  counter = 55;
}

@Component({
  Settings: {
    selector: 'app-root',
  },
  Providers: DefineDependencies(CounterService)(Container),
  State: function(this: AppComponent, [counterService]) {
    return this.subject.pipe(
      map(value => ({
        counter: value + counterService.counter
      }))
    );
  },
  Render: ([counterService]) =>
    function(this, { counter }) {
      return html`
        <p>Count: ${counter}</p>
        <button @click=${() => this.increment()}>
          Increment
        </button>
      `;
    }
})
export class AppComponent extends LitElement {
  @property({ type: Number })
  private count = 0;

  subject = new BehaviorSubject(0)

  increment() {
    this.subject.next(this.count++);
  }
}

Why Choose @rhtml?

Modern web development made simple, powerful, and maintainable

Lightning Fast

Built on Web Components and LitElement for optimal performance. Minimal overhead with maximum efficiency.

🔄

Reactive by Design

Native RxJS integration for reactive programming. Build responsive UIs with declarative data flows.

🎯

Type Safe

Full TypeScript support with excellent IDE integration. Catch errors at compile time, not runtime.

🧩

Component Based

Build reusable, composable components with Web Components standards. Share components across projects easily.

💉

Dependency Injection

Powerful DI container inspired by Angular. Manage services and dependencies with ease.

🚀

Full Stack

Frontend and backend in one framework. Fastify integration with GraphQL, MongoDB, and AMQP support.

More Examples

Real examples showing the power and simplicity of @rhtml

🔄 Reactive State Management

counter.component.ts
import {
  Component,
  DefineDependencies
} from '@rhtml/component';
import { Container, Injectable } from '@rxdi/core';
import { html, LitElement, property } from '@rxdi/lit-html';
import { interval } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
class CounterService {
  counter = 55;
}

@Component({
  Settings: {
    selector: 'counter',
  },
  Providers: DefineDependencies(CounterService)(Container),
  State: function(this: CounterComponent, [counterService]) {
    return interval(1000).pipe(
      map((value) => ({
        counter: this.counter + counterService.counter + value,
      }))
    );
  },
  Render: ([counterService]) =>
    function(this, { counter }) {
      return html`
        <div>
          <h2>Counter: ${counter}</h2>
          <p>Service Counter: ${counterService.counter}</p>
        </div>
      `;
    }
})
export class CounterComponent extends LitElement {
  @property({ type: Number })
  counter: number = 0;
}

🎯 Backend API with Fastify

user.controller.ts
import {
  Controller,
  Route,
  Subscribe
} from '@rhtml/fastify';
import { Subscribe } from '@rhtml/amqp';
import { MessageService } from './message.service';

@Controller()
export class NotificationController {
  constructor(private messageService: MessageService) {}

  @Route({
    url: '/publish-message',
  })
  async sendNotification(notification: any) {
    await this.messageService.publish('my-message-queue', {});
  }

  @Subscribe({
    queue: 'my-message-queue',
    consumeOptions: {
      noAck: false,
    },
  })
  async myMessageQueueSubscription(
    message: ConsumeMessage,
    channel: AmqpChannel
  ) {
    channel.ack();
  }
}

Build scalable backend APIs with @rhtml/fastify and decorators. Get started quickly with the fastify-starter template.

📊 GraphQL Integration

user-list.component.ts
import {
  Component,
  DefineDependencies
} from '@rhtml/component';
import { Container, Injectable } from '@rxdi/core';
import { html, LitElement, property } from '@rxdi/lit-html';
import { of } from 'rxjs';

@Injectable()
class UserService {
  getUser(userId: string) {
    return {
      id: userId,
      name: 'John Doe',
      email: 'john@example.com'
    };
  }
}

@Component({
  Settings: {
    selector: 'user-list',
  },
  Providers: DefineDependencies(UserService)(Container),
  State: function(this: UserComponent, [userService]) {
    return of({
      user: userService.getUser(this.userId),
      time: new Date().getSeconds(),
    });
  },
  Render: ([userService]) =>
    function(this, { user, time }) {
      return html`
        <div>
          <h2>${user.name}</h2>
          <p>Email: ${user.email}</p>
          <p>Current time: ${time}</p>
        </div>
      `;
    }
})
export class UserComponent extends LitElement {
  @property({ type: String })
  userId = '';
}

💾 MongoDB with Mongoose

user.service.ts
import { Injectable, Inject } from '@rhtml/di';
import {
  AmqpChannel,
  AmqpService,
  ConsumeMessage
} from '@rhtml/amqp';

@Injectable()
export class MessageService {
  constructor(private amqpService: AmqpService) {}

  async publish(name: string, payload: any) {
    await this.amqpService.publish(name, {
      payload: {},
    });
  }
}

@Injectable()
export class UserService {
  constructor(
    @Inject(Repositories) private repos: Repositories,
    private messageService: MessageService
  ) {}

  createFile(file: File) {
    return this.repos.file.create(file);
  }

  listFiles() {
    return this.repos.file.find();
  }

Component System Comparison

Choose the right approach for your project

@rxdi/lit-html

First Generation
simple-counter.ts
import { Component, html, LitElement, property } from '@rxdi/lit-html';

@Component({
  selector: 'simple-counter',
  template: () => html`
    <div>
      <h2>Count: ${this.count}</h2>
      <button @click=${this.increment}>
        +1
      </button>
    </div>
  `
})
export class SimpleCounter extends LitElement {
  @property({ type: Number })
  count = 0;

  increment() {
    this.count++;
  }
}
✓ Pure Web Components
✓ Simple API
✓ Maximum flexibility
✓ Direct LitElement inheritance

@rhtml/component

Second Generation
reactive-counter.ts
import { Component, DefineDependencies } from '@rhtml/component';
import { Container, Injectable } from '@rxdi/core';
import { html, LitElement, property } from '@rxdi/lit-html';
import { interval } from 'rxjs';
import { map } from 'rxjs/operators';

@Injectable()
class CounterService {
  counter = 55;
}

@Component({
  Settings: {
    selector: 'reactive-counter',
  },
  Providers: DefineDependencies(CounterService)(Container),
  State: function(this, [counterService]) {
    return interval(1000).pipe(
      map((value) => ({
        counter: this.counter + counterService.counter + value,
      }))
    );
  },
  Render: ([counterService]) =>
    function(this, { counter }) {
      return html`
        <div>
          <h2>Counter: ${counter}</h2>
          <p>Service Counter: ${counterService.counter}</p>
        </div>
      `;
    }
})
export class ReactiveCounter extends LitElement {
  @property({ type: Number })
  counter: number = 0;
}
✓ Functional Reactive
✓ Dependency Injection
✓ Observable State
✓ Advanced Composition

Ready to Build Something Amazing?

Join the community of developers building modern web applications with @rhtml