Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
9edad01
feat: initial work on FormGroupApi in core
crutchcorn Apr 16, 2026
dbc7ff6
chore: initial (and wrong) per-form-group-validation
crutchcorn Apr 16, 2026
1a22f52
chore: create a common FieldLikeAPI to adopt in form groups shortly
crutchcorn Apr 16, 2026
4775996
chore: implement FieldLike API on FormGroup
crutchcorn Apr 16, 2026
756ddf8
chore: revert changes to FormApi validation logic
crutchcorn Apr 16, 2026
c553439
chore: fix type issues with validation kind
crutchcorn Apr 16, 2026
d2eaa29
chore: minor fixes
crutchcorn Apr 16, 2026
ac22e7b
Revert "chore: revert changes to FormApi validation logic"
crutchcorn Apr 16, 2026
c2d061b
chore: filter fields to validate in formgroup
crutchcorn Apr 16, 2026
433ad6e
chore: improve store
crutchcorn Apr 16, 2026
22e49a7
chore: fix tests
crutchcorn Apr 16, 2026
db88246
chore: fix another test
crutchcorn Apr 16, 2026
4eb9b0f
chore: add submitmeta test
crutchcorn Apr 16, 2026
61d36cd
ci: apply automated fixes and generate docs
autofix-ci[bot] Apr 16, 2026
9b8bcb5
chore: add FormLike methods to FormGroup
crutchcorn Apr 20, 2026
86d9000
Merge branch 'form-group' of https://github.com/TanStack/form into fo…
crutchcorn Apr 20, 2026
d990bc6
chore: add type tests
crutchcorn Apr 22, 2026
66c5db9
chore: fix build
crutchcorn Apr 22, 2026
3c8ff5c
chore: fix type tests
crutchcorn Apr 22, 2026
4e4b272
ci: apply automated fixes and generate docs
autofix-ci[bot] Apr 22, 2026
ce63f8f
Merge branch 'main' into form-group
crutchcorn May 6, 2026
6094b9c
chore: export two missing items
crutchcorn May 6, 2026
1f96984
chore: add new interop FormGroupOptions type
crutchcorn May 6, 2026
ee121e4
chore: initial implement of useFormGroup in React
crutchcorn May 6, 2026
c46e342
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
3b756ae
chore: add formGroup to useForm
crutchcorn May 6, 2026
93ec42f
chore: add initial React implementation tests
crutchcorn May 6, 2026
da467f3
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
d1471f6
chore: add type tests for React adapter
crutchcorn May 6, 2026
e3e81b9
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
23e8066
chore: fix eslint
crutchcorn May 6, 2026
c86b424
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
4b81818
chore: add multi-step wizard
crutchcorn May 6, 2026
40b2758
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
e9ee38c
fix: handle resubmissions
crutchcorn May 6, 2026
bfa9464
fix: validate on resubmit
crutchcorn May 6, 2026
a86f781
feat: add better onDynamic handling to FormGroupApi
crutchcorn May 6, 2026
fec4e73
chore: first attempt to fix onDynamic change handling on groups
crutchcorn May 6, 2026
c3d6176
chore: attempt 2
crutchcorn May 6, 2026
973fab7
chore: attempt 3
crutchcorn May 6, 2026
7546f08
chore: attempt 4
crutchcorn May 6, 2026
c8ec12e
Revert "chore: attempt 4"
crutchcorn May 6, 2026
d499d20
chore: finalize fixing form errors
crutchcorn May 6, 2026
58ed8a2
chore: fix eslint
crutchcorn May 6, 2026
f69771b
chore: regenerate lockfile
crutchcorn May 6, 2026
7d9ca0d
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
9e78281
docs: update the wizard example
crutchcorn May 6, 2026
4837dd9
ci: apply automated fixes and generate docs
autofix-ci[bot] May 6, 2026
b189273
chore: initial work on Vue FormGroup API
crutchcorn May 7, 2026
1ee623c
ci: apply automated fixes and generate docs
autofix-ci[bot] May 7, 2026
1061988
Merge branch 'main' into form-group
crutchcorn May 8, 2026
e333776
chore: revert useContext changes
crutchcorn May 8, 2026
ad5bdd7
Merge branch 'main' into form-group
crutchcorn May 8, 2026
9371cfb
feat: add preact form group
crutchcorn May 8, 2026
d9f8097
chore: fix tests and export names
crutchcorn May 8, 2026
fa7ed5e
chore: add Preact wizard demo
crutchcorn May 8, 2026
0fbc378
feat: add FormGroup support for Solid adapter
crutchcorn May 8, 2026
caf608e
chore: add formgroup tests for Solid
crutchcorn May 8, 2026
30f20f2
chore: add type tests for Solid
crutchcorn May 8, 2026
d14c6c7
ci: apply automated fixes and generate docs
autofix-ci[bot] May 8, 2026
5458c9e
chore: add Solid group example
crutchcorn May 8, 2026
dcaebbe
ci: apply automated fixes and generate docs
autofix-ci[bot] May 8, 2026
a9c2e92
chore: fix test
crutchcorn May 8, 2026
71fb870
Merge branch 'main' into form-group
crutchcorn May 9, 2026
c5c3798
Merge branch 'main' into form-group
crutchcorn May 9, 2026
5c6a7e4
feat: add FormGroup impl to Svelte
crutchcorn May 9, 2026
ab4ab8b
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
85a036a
chore: add formGroup tests for Svelte
crutchcorn May 9, 2026
ec039ad
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
9e085a8
chore: add Svelte example
crutchcorn May 9, 2026
5bf025e
chore: fix sherif
crutchcorn May 9, 2026
3f386d9
feat: add Lit FormGroup API
crutchcorn May 9, 2026
2ff22da
chore: add Lit FormGroup tests
crutchcorn May 9, 2026
d711535
chore: refactor Lit tests
crutchcorn May 9, 2026
a8d754f
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
75b8306
feat: implement FormGroup in Angular
crutchcorn May 9, 2026
47a8664
chore: add FormGroup Angular spec
crutchcorn May 9, 2026
3a18322
ci: apply automated fixes and generate docs
autofix-ci[bot] May 9, 2026
53412be
feat: add withForm feature to Angular
crutchcorn May 9, 2026
02ca57e
chore: update Angular large-form example to use new `withForm` API
crutchcorn May 9, 2026
8cc45ec
docs: add docs to Angular withForm example
crutchcorn May 9, 2026
3a6f632
chore: add changeset
crutchcorn May 9, 2026
6edf9d4
chore: fix knip
crutchcorn May 9, 2026
596c8d5
chore: initial multi-step-wizard Angular example
crutchcorn May 9, 2026
001068c
Merge branch 'angular-with-form' into form-group
crutchcorn May 9, 2026
31c616a
chore: update the multi-step-wizard to use new API
crutchcorn May 9, 2026
4bc606c
Merge branch 'main' into form-group
crutchcorn May 9, 2026
771ff94
Merge branch 'main' into form-group
crutchcorn May 10, 2026
36f775c
Merge branch 'main' into form-group
crutchcorn May 10, 2026
db61223
chore: add multi-step-wizard example to Lit
crutchcorn May 10, 2026
9f68cbd
ci: apply automated fixes and generate docs
autofix-ci[bot] May 10, 2026
87d61f5
chore: add Vue example
crutchcorn May 10, 2026
c05d2ac
chore: fix CI
crutchcorn May 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/swift-suns-remain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@tanstack/angular-form': minor
---

Add tanstack-with-form API for more modular form usage
16 changes: 16 additions & 0 deletions examples/angular/multi-step-wizard/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Editor configuration, see https://editorconfig.org
root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.ts]
quote_type = single

[*.md]
max_line_length = off
trim_trailing_whitespace = false
42 changes: 42 additions & 0 deletions examples/angular/multi-step-wizard/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.

# Compiled output
/dist
/tmp
/out-tsc
/bazel-out

# Node
/node_modules
npm-debug.log
yarn-error.log

# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*

# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings

# System files
.DS_Store
Thumbs.db
27 changes: 27 additions & 0 deletions examples/angular/multi-step-wizard/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Simple

This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 17.0.1.

## Development server

Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The application will automatically reload if you change any of the source files.

## Code scaffolding

Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.

## Build

Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.

## Running unit tests

Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).

## Running end-to-end tests

Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.

## Further help

To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
78 changes: 78 additions & 0 deletions examples/angular/multi-step-wizard/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"simple": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/simple",
"index": "src/index.html",
"browser": "src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.app.json",
"assets": ["src/favicon.ico", "src/assets"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "simple:build:production"
},
"development": {
"buildTarget": "simple:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "simple:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "tsconfig.spec.json",
"assets": ["src/favicon.ico", "src/assets"],
"scripts": []
}
}
}
}
}
}
33 changes: 33 additions & 0 deletions examples/angular/multi-step-wizard/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@tanstack/form-example-angular-multi-step-wizard",
"private": true,
"type": "module",
"scripts": {
"ng": "ng",
"start": "ng cache clean && ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"dependencies": {
"@angular/animations": "^20.3.1",
"@angular/common": "^20.3.1",
"@angular/compiler": "^20.3.1",
"@angular/core": "^20.3.1",
"@angular/forms": "^20.3.1",
"@angular/platform-browser": "^20.3.1",
"@angular/platform-browser-dynamic": "^20.3.1",
"@angular/router": "^20.3.1",
"@tanstack/angular-form": "^1.31.0",
"rxjs": "^7.8.2",
"tslib": "^2.8.1",
"zod": "^3.25.76",
"zone.js": "0.15.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^20.3.2",
"@angular/cli": "^20.3.2",
"@angular/compiler-cli": "^20.3.1",
"typescript": "5.8.2"
}
}
61 changes: 61 additions & 0 deletions examples/angular/multi-step-wizard/src/app/app.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Component, signal } from '@angular/core'
import {
TanStackWithForm,
injectForm,
injectStore,
revalidateLogic,
} from '@tanstack/angular-form'
import { z } from 'zod'
import { Step1Component } from './step1.component'
import { Step2Component } from './step2.component'
import { step1Schema, step2Schema, wizardFormOpts } from './shared-form'

@Component({
selector: 'app-root',
standalone: true,
imports: [TanStackWithForm, Step1Component, Step2Component],
template: `
@if (step() === 0) {
<app-step1
tanstack-with-form
[form]="form"
[step]="step()"
[isSubmitting]="isSubmitting()"
(stepChange)="step.set($event)"
/>
}
@if (step() === 1) {
<app-step2
tanstack-with-form
[form]="form"
[step]="step()"
[isSubmitting]="isSubmitting()"
(stepChange)="step.set($event)"
/>
}
`,
})
export class AppComponent {
step = signal(0)

form = injectForm({
...wizardFormOpts,
validationLogic: revalidateLogic(),
// onDynamic is only used when `form.handleSubmit` is called itself.
// When the FormGroup's `handleSubmit` is called, it will only validate the
// current step's schema. This means that this schema will not be called
// when the user submits the form group, but instead when they submit the
// entire form.
validators: {
onDynamic: z.object({
step1: step1Schema,
step2: step2Schema,
}),
},
onSubmit: ({ value }) => {
alert(`Form submitted: ${JSON.stringify(value)}`)
},
})

isSubmitting = injectStore(this.form, (state) => state.isSubmitting)
}
21 changes: 21 additions & 0 deletions examples/angular/multi-step-wizard/src/app/shared-form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { formOptions } from '@tanstack/angular-form'
import { z } from 'zod'

export const step1Schema = z.object({
name: z.string().min(2, 'Name must be at least 2 characters'),
})

export const step2Schema = z.object({
name: z.string().min(3, 'Name must be at least 3 characters'),
})

export const wizardFormOpts = formOptions({
defaultValues: {
step1: {
name: '',
},
step2: {
name: '',
},
},
})
69 changes: 69 additions & 0 deletions examples/angular/multi-step-wizard/src/app/step1.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Component, input, output } from '@angular/core'
import {
TanStackAppField,
TanStackField,
TanStackFormGroup,
injectWithForm,
} from '@tanstack/angular-form'
import { TextFieldComponent } from './text-field.component'
import { step1Schema, wizardFormOpts } from './shared-form'

@Component({
selector: 'app-step1',
standalone: true,
imports: [
TanStackField,
TanStackAppField,
TanStackFormGroup,
TextFieldComponent,
],
template: `
<ng-container
[tanstackFormGroup]="withForm.form"
name="step1"
[validators]="{ onDynamic: step1Schema }"
[onGroupSubmit]="onGroupSubmit"
[onGroupSubmitInvalid]="onGroupSubmitInvalid"
#group="formGroup"
>
<form
(submit)="
$event.preventDefault();
$event.stopPropagation();
group.api.handleSubmit()
"
>
<app-text-field
label="Step 1 Name"
tanstack-app-field
[tanstackField]="withForm.form"
name="step1.name"
/>

<button type="submit" [disabled]="isSubmitting()">Submit</button>

<!-- formGroup contains errorMaps and errors, just like forms and fields -->
<pre>{{ stringify(group.api.state.meta.errorMap) }}</pre>
</form>
</ng-container>
`,
})
export class Step1Component {
withForm = injectWithForm({ ...wizardFormOpts })
step = input.required<number>()
isSubmitting = input.required<boolean>()
stepChange = output<number>()

step1Schema = step1Schema
stringify = (value: unknown) => JSON.stringify(value, null, 2)

onGroupSubmit = () => {
this.stepChange.emit(this.step() + 1)
}

onGroupSubmitInvalid = () => {
// Just like a form, you can also handle invalid submits at the group level,
// which is useful for multi-step wizards to prevent going to the next step
// if the current step is invalid
}
}
Loading
Loading