Angular components that bind straight to Signal Forms.

Zoneless, SSR-safe, tree-shakable — with no accessor layer in between.

Not in the library:ControlValueAccessor

How it compares to the other Angular UI libraries
Components

Composed in context.

Forms, navigation, data, feedback — each primitive stitched into a real scene. Type into the inputs, toggle the switches, flip the theme: every tile is live state, not a screenshot.

Create account

Form field + input + button

Active users

Counter + delta chip

42.1K
▲ 12.4%

+5.2K this week

Amount

Input group + prefix

$
Pay now

Notifications

Switch stack

Push notifications

Real-time activity

Daily digest

8:00 AM summary

Beta features

Early access

Notification

Alert surface

Payment received

$129.99 from Roman

2 new comments

View thread

Empty state

Empty primitive

No projects yet
Create your first project to get started.
New project

Team

Avatar stack + badge

EmeliaJMAKRT+4

8 collaborators

Synced

Topics

Tag cluster

AngularStandaloneSignalsSSRZonelessA11y

Tabbed view

Tabs + content

Standalone components, signals-first.

Page

Pagination

1 2 3 4 5 8

Showing page 2 of 8

Profile

Form field + hint

3–20 chars, no spaces.
Public, lowercase.

Theming

Segmented drives the page theme

Search

Input + kbd shortcut

K

Press ⌘K from anywhere on the docs.

Loading

Skeleton placeholders

Profile

Avatar + meta + tags

Avatar

Roman Kim

Founding engineer

AngularLeadAsia/Tashkent
Follow

Install

Drop-in component

import { WrButton } from 'ngwr/button';

@Component({
  imports: [WrButton],
  template: `<wr-btn color="primary">Save</wr-btn>`,
})
export class Demo {}

Each entry point is its own ng-packagr build.

Developer experience

Signal Forms, with nothing in between.

A value control here is not a wrapper around a native element that reports back through an accessor. It implements FormValueControl or FormCheckboxControl, so the field binds to the component's own value / checked model directly — and <wr-form-field> already knows how to word the error.

  • Nineteen value controls implement a Signal Forms interface — zero ControlValueAccessor in the library
  • Classic [(ngModel)] and reactive forms still work; standalone use is the two-way [(value)] / [(checked)]
  • Zoneless by construction — signal inputs, signal state, afterNextRender() for DOM work
  • 202 tree-shakable entry points — each component is its own ng-packagr entry, so only what you import ships
  • SSR-safe by construction — this site prerenders every route, and a prerender error fails the build
import { Component, signal } from '@angular/core';
import { FormField, email, form, required } from '@angular/forms/signals';

import { WrButton } from 'ngwr/button';
import { WrCheckbox } from 'ngwr/checkbox';
import { WrFormField } from 'ngwr/form';
import { WrInput } from 'ngwr/input';

@Component({
  selector: 'signup-card',
  imports: [FormField, WrFormField, WrInput, WrCheckbox, WrButton],
  template: `
    <wr-form-field label="Work email" required>
      <input wrInput type="email" [formField]="signup.email" />
    </wr-form-field>

    <wr-checkbox [formField]="signup.agree">I agree to the terms</wr-checkbox>

    <wr-btn color="primary" block (click)="reserve()">
      Reserve your spot
    </wr-btn>
  `,
})
export class SignupCard {
  private readonly model = signal({ email: '', agree: false });

  // `signup.email` writes the input's value and `signup.agree` the checkbox's
  // `checked` model. No ControlValueAccessor — there is not one in the library.
  // The "required" and "not an email" copy comes from the i18n catalog, so no
  // <wr-form-error> has to be written unless this field wants its own wording.
  protected readonly signup = form(this.model, path => {
    required(path.email);
    email(path.email);
  });

  protected reserve(): void {}
}
Why ngwr

Built on the new Angular. Not ported to it.

Nothing here carries a compatibility layer from the NgModule era. The bookkeeping that is left — focus traps, virtual scroll, density tokens, error copy — is handled inside the primitive.

Signal Forms, natively

Nineteen value controls implement a Signal Forms interface, so `[formField]` binds to the component's own value or checked model. No `ControlValueAccessor` anywhere in the library.

Zoneless by construction

Not zoneless-compatible — zoneless in the source. Signal inputs, signal state, `afterNextRender()` for DOM work, and zero `@NgModule`s or `@Input()` decorators.

A11y is gated, not claimed

Three axe gates run against the built site: structure on every PR, painted contrast and in-overlay states nightly. A serious violation is a red build.

Themed by tokens, not overrides

Color, radius, spacing, and duration come from `--wr-*` custom properties — and a token nothing paints with has to justify itself to the lint run. Re-skin one component or all of them.

Pay for what you import

202 entry points, each its own ng-packagr build. What you don't import never lands in your bundle — the SCSS opts in per component too.

Dark mode is first-class

`[data-theme]` flips at the root or any subtree. `prefers-color-scheme` is one provider option, not a hardcoded check.

Animations

Motion you can ship.

Beyond the primitives — a kit of motion components for hero copy, hovers, and the bits of personality that set a real product apart.

Premium

Shiny text

<wr-shiny-text />

System online

Glitch text

<wr-glitch-text />

Fast

Rotating text

<wr-rotating-text />

Decrypting…

Decrypt text

<wr-decrypt-text />

Reveal on view

Blur text

<wr-blur-text />

Split by character

Split text

<wr-split-text />

Typing words…

Typewriter

<wr-typewriter />

Gradient flow

Gradient text

<wr-gradient-text />