SkillAgentSearch skills...

Rhtml

Reactive HyperText Markup Language https://r-html.github.io/rhtml/

Install / Use

/learn @r-html/Rhtml
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Reactive HTML

Documentation https://r-html.github.io/rhtml/docs/intro/

Packages

| Package | Description | | ------------------------------------------------------ | ---------------------------------------------------------------------- | | @rhtml/di | IOC container | | @rhtml/component | Main reactive component for building UI | | @rhtml/components | Declarative monadic approach defining WC using html | | @rhtml/operators | Useful declarative operators like for if | | @rhtml/graphql | Declarative Graphql for executing query mutation or subscription | | @rhtml/hooks | React like hooks for use inside web components | | @rhtml/renderer | Main renderer for every component used also with observables | | @rhtml/schematics | Angular like schematics for component generation using DI container | | @rhtml/experiments | Declarative way of defining web components only with HTML | | @rhtml/decorators | Useful decorators @HostListener and @Input | | @rhtml/modifiers | Modifiers created using Custom HTML Attributes | | @rhtml/custom-attributes | Create your own custom Attributes |

Installation

npm i @rhtml/operators @rhtml/components @rhtml/hooks @rhtml/graphql

Usage

import { LitElement, Component, html } from '@rxdi/lit-html';
import { BehaviorSubject } from 'rxjs';
import { delay } from 'rxjs/operators';

import '@rhtml/operators';
import '@rhtml/components';
import '@rhtml/hooks';
import '@rhtml/graphql';

interface State {
  counter: number;
}
interface NotificationState {
  data: { notifications: { appUpdated: string | number } };
}
@Component({
  selector: 'r-html-view',
  template(this: RHtmlViewComponent) {
    return html`
      <r-renderer
        .options=${{
          state: new BehaviorSubject({ counter: 1 }).pipe(delay(1700)),
          render: (res: State, setState: (res: State) => State) =>
            html`
              <button
                @click=${() => setState({ counter: res.counter + res.counter })}
              >
                Increment
              </button>
              ${res.counter}
            `,
          loading: () => html` Loading... `,
          error: () => html` Error `,
        }}
      ></r-renderer>

      <r-for .of=${['IterableItem 1', 'Iterable Item 2']}>
        <r-let .item=${(v) => html` ${v} `}></r-let>
      </r-for>

      <r-part>
        <r-state .value=${'Kristiyan Tachev'}></r-state>
        <r-render .state=${(name) => html` <p>${name}</p> `}> </r-render>
      </r-part>

      <r-part>
        <r-settings .value=${{ fetchPolicy: 'cache-first' }}></r-settings>
        <r-fetch .query=${`{ continents { name } }`}></r-fetch>
        <r-render
          .state=${({ data: { continents } }) => html`
            <r-for .of=${continents}>
              <r-let .item=${({ name }) => name}></r-let>
            </r-for>
          `}
        >
        </r-render>
      </r-part>

      <r-part>
        <r-fetch .subscribe=${`{ notifications { appUpdated } }`}></r-fetch>
        <r-render
          .state=${(
            {
              data: {
                notifications: { appUpdated },
              },
            },
            setState: (s: NotificationState) => void
          ) => html`
            <p>${appUpdated}</p>
            <button
              @click=${() => {
                setState({
                  data: {
                    notifications: {
                      appUpdated: Number(appUpdated) + Number(appUpdated),
                    },
                  },
                });
              }}
            >
              Increment Subscriptions State x2
            </button>
            (will be overriten when server emit new state)
          `}
        >
        </r-render>
      </r-part>
    `;
  },
})
export class RHtmlViewComponent extends LitElement {}

Setup Graphql Client

To set configuration on bundle time we need to get settings without barrel export, this way we can set configuration before Graphql module loads configuration Keep it in mind that this is the default configuration for GraphqlClient

import { setConfig } from '@rhtml/graphql/settings';

setConfig({
  config: {
    uri: 'https://countries.trevorblades.com/',
    pubsub: 'wss://pubsub.youvolio.com/subscriptions',
    async onRequest() {
      return new Headers();
    },
  },
  defaults: {
    error: (e) => {
      return html` <p style="color: black">${e}</p> `;
    },
    loading: () => {
      return html`
        <div style="text-align: center;">
          <rx-loading palette="danger"></rx-loading>
        </div>
      `;
    },
  },
});

import '@rhtml/graphql';

Later on you can use r-fetch component to specify query, mutation, subscription

<r-part>
  <r-fetch .subscribe=${`{ notifications { appUpdated } }`}></r-fetch>
  <r-render .state=${({ data: { notifications: { appUpdated } } }, setState: (s: NotificationState) => void) => html`
    <p>${appUpdated}</p>
    <button
      @click=${() => {
        setState({
          data: {
            notifications: {
              appUpdated: Number(appUpdated) + Number(appUpdated)
            }
          }
        });
      }}
    >
      Increment Subscriptions State x2
    </button>
    (will be overriten when server emit new state)
  `}>
  </r-render>
</r-part>
Dependency Injection
npm i @rhtml/di
import '@abraham/reflection';

import { Inject, Injectable, InjectionToken } from '@rhtml/di';
import { Bootstrap, Component, Module } from '@rhtml/di/module';

type UserId = number;
const UserId = new InjectionToken<UserId>();

const now = Date.now();

@Injectable()
export class UserService {
  constructor(@Inject(UserId) public userId: number) {
    console.log('[UserService]', userId);
  }
}

@Component()
class AppComponent {
  constructor(public userService: UserService) {
    console.log('[AppComponent] ', userService.userId);
  }

  OnInit() {
    console.log('[AppComponent] Init');
  }

  OnDestroy() {
    console.log('[AppComponent] Destroy');
  }
}

@Module({
  providers: [
    UserService,
    {
      provide: UserId,
      useFactory: () =>
        new Promise<number>((resolve) => setTimeout(() => resolve(1234), 1000)),
    },
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

Bootstrap(AppModule).then(() =>
  console.log('Started', `after ${Date.now() - now}`)
);

@rhtml - Modern Web Components Framework

A powerful, type-safe framework for building modern web applications using Web Components with the simplicity of React and the power of TypeScript.

🚀 Features

  • 🎯 Type-Safe Templates - Full TypeScript support in your templates
  • 🧩 True Encapsulation - Leveraging Web Components standards
  • 🎨 Scoped Styles - CSS encapsulation without leaks
  • Reactive by Design - Efficient updates with fine-grained reactivity
  • 🛠 Powerful Modifiers - Flexible layout system with Angular-inspired modifiers
  • 🏗 Component-First - Everything is a component
  • 📦 Zero Configuration - Works out of the box
  • 🔍 Developer Friendly - Excellent IDE support and debugging experience

📦 Installation

npm install @rxdi/lit-html @rhtml/modifiers

🎯 Quick Start

import { Component, css, html, LitElement } from '@rxdi/lit-html';
import { FlexLayout } from '@rhtml/modifiers';

@Component({
  selector: 'hello-world',
  styles: [
    css`
      :host {
        display: block;
      }
      .greeting {
        color: var(--primary-color, #2196f3);
      }
    `,
  ],
  modifiers: [...FlexLayout],
  template(this: HelloWorld) {
    return html`
      <div class="greeting" fxLayout="row">Hello, ${this.name}!</div>
    `;
  },
})
export class HelloWorld extends LitElement {
  @property()
  name: string = 'World';
}

🎨 Key Concepts

Type-Safe Templates

@rhtml ensures type safety in your templates by leveraging TypeScript:

// ✅ Good Practice - Type-safe template
@Component({
  template(this: MyComponent) {
    return html`
      <div>${this.message}</div>
      <button @click=${() => this.handleClick()}>Click me</button>
    `;
  }
})
export class MyComponent extends LitElement {
  @property()
  message: string = 'Hello';

  handleClick() {
    console.log('Clicked!');
  }
}

// ❌ Bad Practice - No type checking
@Component({
  template() { // Missing type information
    return html`
      <div>${this.message}</div> // TypeScript can't verify this exists
    `;
  }
})

Component Registration

Components must be explicitly registered to be used:

@Component({
  selector: 'app-root',
  components: [ChildComponent], // Register child components
  template(this: AppRoot) {
    return html` <child-component></child-component> `;
  },
})
export class AppRoot extends LitElement {}

Reactive Properties

@rhtml provides a simple yet powerful reactivity system:

export class UserProfile extends LitElement {
  // Simple property
  @property()
  name: string = '';

  // Property with options
  @property({
    type: Number,
    reflect: true, // Reflects to attribute
View on GitHub
GitHub Stars12
CategoryDevelopment
Updated12d ago
Forks0

Languages

TypeScript

Security Score

75/100

Audited on Mar 25, 2026

No findings