Create account
Form field + input + button
Zoneless, SSR-safe, tree-shakable — with no accessor layer in between.
Not in the library:
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
+5.2K this week
Amount
Input group + prefix
Notifications
Switch stack
Push notifications
Real-time activity
Daily digest
8:00 AM summary
Beta features
Early access
Notification
Alert surface
Payment received
2 new comments
Empty state
Empty primitive
Team
Avatar stack + badge
Topics
Tag cluster
Tabbed view
Tabs + content
Standalone components, signals-first.
Page
Pagination
Showing page 2 of 8
Profile
Form field + hint
Theming
Segmented drives the page theme
Search
Input + kbd shortcut
Press ⌘K from anywhere on the docs.
Loading
Skeleton placeholders
Profile
Avatar + meta + tags
Roman Kim
Founding engineer
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.
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.
ControlValueAccessor in the library [(ngModel)] and reactive forms still work; standalone use is the two-way [(value)] / [(checked)]afterNextRender() for DOM work 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 {}
}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.
Beyond the primitives — a kit of motion components for hero copy, hovers, and the bits of personality that set a real product apart.
Shiny text
<wr-shiny-text />
Glitch text
<wr-glitch-text />
Rotating text
<wr-rotating-text />
Decrypt text
<wr-decrypt-text />
Blur text
<wr-blur-text />
Split text
<wr-split-text />
Typewriter
<wr-typewriter />
Gradient text
<wr-gradient-text />