Page actions

Quick Start

Render a JSON spec as a live Angular component tree in 5 minutes.

Note: Prerequisites

Angular 20–22 project with @threadplane/render installed. See the Installation guide if you need setup help.

1
Create a component to render

Start with a simple Angular component for the spec to render. Every rendered component receives inputs matching the AngularComponentInputs interface -- your custom props are spread alongside the standard inputs.

// text.component.ts
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
import type { Spec } from '@json-render/core';
 
@Component({
  selector: 'app-text',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `<p>{{ label() }}</p>`,
})
export class TextComponent {
  readonly label = input<string>('');
  readonly childKeys = input<string[]>([]);
  readonly spec = input.required<Spec>();
}
2
Define a registry

Map element type names to Angular component classes using defineAngularRegistry().

// ui-registry.ts
import { defineAngularRegistry } from '@threadplane/render';
import { TextComponent } from './text.component';
 
export const uiRegistry = defineAngularRegistry({
  Text: TextComponent,
});
3
Build a spec

Now describe the UI tree itself. A spec is a flat map of elements, and the root key points to the entry element.

// app.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { RenderSpecComponent } from '@threadplane/render';
import type { Spec } from '@json-render/core';
import { uiRegistry } from './ui-registry';
 
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RenderSpecComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <render-spec [spec]="spec" [registry]="registry" />
  `,
})
export class AppComponent {
  registry = uiRegistry;
 
  spec: Spec = {
    root: 'greeting',
    elements: {
      greeting: {
        type: 'Text',
        props: { label: 'Hello from @threadplane/render!' },
      },
    },
  };
}
4
Run your app
ng serve

Open http://localhost:4200. You should see "Hello from @threadplane/render!" rendered by your TextComponent.

Adding Reactive State

Specs get a lot more interesting once you connect them to a state store. Props with $state expressions read from the store reactively.

import { Component, ChangeDetectionStrategy } from '@angular/core';
import { RenderSpecComponent, signalStateStore } from '@threadplane/render';
import type { Spec } from '@json-render/core';
import { uiRegistry } from './ui-registry';
 
@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RenderSpecComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `
    <render-spec [spec]="spec" [registry]="registry" [store]="store" />
    <button (click)="updateName()">Change Name</button>
  `,
})
export class AppComponent {
  registry = uiRegistry;
  store = signalStateStore({ name: 'World' });
 
  spec: Spec = {
    root: 'greeting',
    elements: {
      greeting: {
        type: 'Text',
        props: { label: { $state: '/name' } },
      },
    },
  };
 
  updateName() {
    this.store.set('/name', 'Angular');
  }
}

Clicking the button updates the store, which reactively updates the rendered TextComponent label from "World" to "Angular".

Using Spec-Embedded State

You can also embed initial state right in the spec. RenderSpecComponent creates an internal signalStateStore from spec.state when neither a [store] input nor a store in provideRender() is present. The internal store is created once for the component instance and reused across spec changes -- a later spec's state is not re-applied.

spec: Spec = {
  root: 'greeting',
  elements: {
    greeting: {
      type: 'Text',
      props: { label: { $state: '/message' } },
    },
  },
  state: {
    message: 'State embedded in the spec!',
  },
};
<!-- No [store] input needed -- an internal store is created from spec.state -->
<render-spec [spec]="spec" [registry]="registry" />

Next Steps

Registry Guide

Register multiple components and learn the input contract

Specs Guide

Spec format, conditional rendering, and repeat loops

State Store Guide

Reactive state management with signalStateStore()

Events Guide

Event handlers and action dispatch

Looking for something specific?