Angular form fields you can actually theme, configure and customize. Validated by whatever you already use.
Created with ❤️ by Cynthion
Eleven form fields, one decorator that puts labels, prefixes, suffixes, hints and messages around them, and around two hundred CSS custom properties to make them look like your product instead of like a component library. The fields bind to Signal Forms, reactive forms and template-driven forms alike; the model and the rules stay with your forms API, and the rules can be Angular's own, Vest, Zod, or none at all.
- Studio: a live form of every field. Theme it to your brand, configure the fields, and take away the CSS and the Signal Forms component.
- Specimen: every field, one change at a time, covering every state, every label position, every adornment and every panel, under any preset or your own theme.
- Docs: the guides and references below, rendered in the browser from the same files.
.documentation/holds them, plus the design notes for maintainers.
- Features
- When To Pick This Over Angular Material
- Installation
- Setup
- Your First Form
- What's In The Box
- Documentation
- Contributing
- License
- 🧩 Eleven Fields, One Decorator: input, textarea, select, dropdown, autocomplete, radio and checkbox group, date, time, toggle, slider.
<formidable-field-decorator>puts the label, prefixes, suffixes, hints, the required marker and the messages around any of them, in six label positions. - 🔌 Every Angular Forms API: Signal Forms, reactive forms and template-driven forms, through
[formField],[formControl],formControlNameorngModel. No value accessor, no form directive of its own, no store. - ✅ Bring Your Own Validator: Angular's own rules, Vest or Zod through Standard Schema, or none. The library renders the messages, decides when they appear, and marks what is required.
- 🎨 Themeable To The Corner: ~200 CSS custom properties and no design system in your bundle. Rebrand from one variable; no SCSS hooks, no theme to initialise.
- 🧠 Typed End To End: Signal Forms types every
[formField]path from your model, so a typo in a model key fails the build. - ⌨️ Accessible By Default: full keyboard handling, managed focus, combobox, listbox, switch and group roles, and messages in an
aria-liveregion. - 🛡️ Masking, Dates And Panels: ngx-mask on text fields, one token string for parsing and formatting a date or time, and panels that flip when there is no room and become a sheet on phones.
- 🛠️ Extensible:
BaseFieldmakes a field of your own bind, decorate and theme like a built-in one. - 🎛️ Studio: build the theme and the form against the real components in the browser, then copy out the CSS and the Signal Forms component, template and schema.
Material is a design system with a form library in it. This is a form library with no design opinion. That is the whole difference, and it cuts both ways.
| You Want | Pick |
|---|---|
| Fields that look like your brand, themed from CSS variables only | ngx-formidable |
| Masking, hints, six label positions and clickable adornments built in | ngx-formidable |
| A form library that adds no design system to your bundle | ngx-formidable |
| Material Design, and to look like it | Angular Material |
| Components beyond forms: tables, dialogs, menus, navigation | Angular Material |
| The CDK: overlays, drag and drop, virtual scroll, a11y utilities | Angular Material |
| A large ecosystem, many maintainers and a long support horizon | Angular Material |
npm i @cynthion/ngx-formidable date-fns ngx-mask pikadayThe library does not validate, so it brings no validation library. Add one only if you want it (npm i vest or npm i zod), or validate with Angular's own rules and add nothing.
Full instructions, including what each peer dependency is for: Getting Started.
// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideNgxFormidable } from '@cynthion/ngx-formidable';
export const appConfig: ApplicationConfig = {
providers: [...provideNgxFormidable()]
};It takes an optional config, including app-wide defaults for what every template would otherwise repeat: the label position, the adornment alignment, the panel position, when messages appear and whether required markers show. See Getting Started.
Then the stylesheet, which is imported separately because it is a stylesheet and not a provider:
// styles.scss
@use '@cynthion/ngx-formidable/styles/ngx-formidable';That is the whole default theme. Redeclare whatever you want to change in your own :root afterwards:
:root {
--formidable-color-field-border-focus: #0f766e; // rebrand from this one variable
--formidable-field-height: 50px;
}Declare the model, its initial value and its rules in one *.form.ts. This one validates with Angular's own rules:
// user.form.ts
import { required, schema } from '@angular/forms/signals';
export interface UserModel {
name: string;
birthdate: Date | null;
}
export const initialUserModel: UserModel = { name: '', birthdate: null };
export const userSchema = schema<UserModel>((path) => {
required(path.name, { message: 'Name is required.' });
});The component holds the model and the form over it:
// user-form.ts
import { Component, signal } from '@angular/core';
import { form, FormField, FormRoot } from '@angular/forms/signals';
import { DateField, FieldDecorator, FieldHint, FieldLabel, InputField } from '@cynthion/ngx-formidable';
import { initialUserModel, UserModel, userSchema } from './user.form';
@Component({
selector: 'app-user-form',
templateUrl: './user-form.html',
imports: [FormRoot, FormField, DateField, FieldDecorator, FieldHint, FieldLabel, InputField]
})
export class UserForm {
readonly model = signal<UserModel>(initialUserModel);
readonly form = form(this.model, userSchema);
}Then the template:
<!-- user-form.html -->
<form [formRoot]="form">
<formidable-field-decorator>
<formidable-input-field [formField]="form.name" />
<div formidableFieldLabel>Name</div>
<div formidableFieldHint>As it appears on your passport</div>
</formidable-field-decorator>
<formidable-field-decorator>
<formidable-date-field
[unicodeTokenFormat]="'dd.MM.yyyy'"
[formField]="form.birthdate" />
<div
formidableFieldLabel
[position]="'border'">
Birthdate
</div>
</formidable-field-decorator>
</form>required() marks the name required and checks it, and its message appears once the user has left the field. Vest and Zod plug into the same schema(), and reactive and template-driven forms bind the same fields: Validation, Forms.
The whole walkthrough, with submission and where each piece goes: Getting Started.
The full API (every input, output, type and token) is in the Component Catalogue.
| Category | Component | Value |
|---|---|---|
| Text | <formidable-input-field> |
string |
<formidable-textarea-field> |
string |
|
| Options | <formidable-select-field> |
string | null |
<formidable-dropdown-field> |
string | null |
|
<formidable-autocomplete-field> |
string | null |
|
| Option Groups | <formidable-radio-group-field> |
string | null |
<formidable-checkbox-group-field> |
string[] |
|
| Date & Time | <formidable-date-field> |
Date | null |
<formidable-time-field> |
Date | null |
|
| Values | <formidable-toggle-field> |
boolean |
<formidable-slider-field> |
number |
|
| Structural | <formidable-field-decorator> |
|
<formidable-field-option> |
||
<formidable-field-errors> |
| Category | Directives And Tokens |
|---|---|
| Decoration | formidableFieldLabel, formidableFieldLabelAdornment, formidableFieldPrefix, formidableFieldSuffix, formidableFieldHint, formidableFieldToggleIcon |
| Configuration | provideNgxFormidable(), FORMIDABLE_DEFAULTS, FORMIDABLE_ERROR_MESSAGE |
Guides teach a topic; references list what it accepts.
| Guide | Covers |
|---|---|
| Getting Started | Install, wiring, the stylesheet, a first form |
| Forms | How the fields meet Signal Forms, reactive forms and template-driven forms |
| Fields | Options, panels, keyboard, dates and times, masking, focus |
| Decoration | Labels, adornments, prefixes, suffixes, hints, required marker |
| Validation | Angular's rules, Vest, Zod or none; messages and their reveal |
| Theming | The default theme, what to override, worked examples |
| Studio | Building a theme and a form in the browser, and exporting both |
| Custom Fields | Building a field or an option of your own |
| Reference | Lists |
|---|---|
| Components | Every component, directive, token, type and interface |
| Theme Reference | Every overridable --formidable-* custom property |
Design notes for maintainers live in .documentation/tech/, and the repo's own conventions in .documentation/impl/.
Contributions are welcome, see CONTRIBUTING.md.
Everything in this repository is licensed under the MIT License unless otherwise specified.
In plain English: use it commercially, modify it, ship it inside a closed-source product, sublicense it. Nothing has to be published back. The one condition is that the copyright notice and the license text travel with any copy or substantial portion of the code. It comes with no warranty and no liability.
Every runtime peer dependency is permissive too (MIT, 0BSD or Apache-2.0, no copyleft anywhere), so adding this library puts no obligation on you beyond MIT's own notice.
Copyright (c) 2025 - present Christian Lüthold