Skip to content

Commit

Permalink
docs: include example of React.lazy for tree-shaking (#1198)
Browse files Browse the repository at this point in the history
  • Loading branch information
crutchcorn authored Mar 2, 2025
1 parent 8105638 commit 230c2a0
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 32 deletions.
67 changes: 67 additions & 0 deletions docs/framework/react/guides/form-composition.md
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,73 @@ const ChildForm = withForm({
})
```

# Tree-shaking form and field components

While the above examples are great for getting started, they're not ideal for certain use-cases where you might have hundreds of form and field components.
In particular, you may not want to include all of your form and field components in the bundle of every file that uses your form hook.

To solve this, you can mix the `createFormHook` TanStack API with the React `lazy` and `Suspense` components:

```typescript
// src/hooks/form-context.ts
import { createFormHookContexts } from '@tanstack/react-form'

export const { fieldContext, useFieldContext, formContext, useFormContext } =
createFormHookContexts()
```

```tsx
// src/components/text-field.tsx
import { useFieldContext } from '../hooks/form-context.tsx'

export default function TextField({ label }: { label: string }) {
const field = useFieldContext<string>()

return (
<label>
<div>{label}</div>
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</label>
)
}
```

```tsx
// src/hooks/form.ts
import {lazy} from 'react'
import {createFormHook} from '@tanstack/react-form'

const TextField = lazy(() => import('../components/text-fields.tsx'))

const { useAppForm, withForm } = createFormHook({
fieldContext,
formContext,
fieldComponents: {
TextField
},
formComponents: {},
})
```

```tsx
// src/App.tsx
import { Suspense } from 'react'
import { PeoplePage } from './features/people/page.tsx'

export default function App() {
return (
<Suspense fallback={<p>Loading...</p>}>
<PeopleForm />
</Suspense>
)
}
```

This will show the Suspense fallback while the `TextField` component is being loaded, and then render the form once it's loaded.

# Putting it all together

Now that we've covered the basics of creating custom form hooks, let's put it all together in a single example.
Expand Down
5 changes: 3 additions & 2 deletions examples/react/large-form/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Suspense } from 'react'
import { PeoplePage } from './features/people/page.tsx'

export default function App() {
return (
<div>
<Suspense fallback={<p>Loading...</p>}>
<PeoplePage />
</div>
</Suspense>
)
}
25 changes: 25 additions & 0 deletions examples/react/large-form/src/components/text-fields.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useStore } from '@tanstack/react-form'
import { useFieldContext } from '../hooks/form-context.tsx'

export default function TextField({ label }: { label: string }) {
const field = useFieldContext<string>()

const errors = useStore(field.store, (state) => state.meta.errors)

return (
<div>
<label>
<div>{label}</div>
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</label>
{errors.map((error: string) => (
<div key={error} style={{ color: 'red' }}>
{error}
</div>
))}
</div>
)
}
4 changes: 4 additions & 0 deletions examples/react/large-form/src/hooks/form-context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { createFormHookContexts } from '@tanstack/react-form'

export const { fieldContext, useFieldContext, formContext, useFormContext } =
createFormHookContexts()
34 changes: 4 additions & 30 deletions examples/react/large-form/src/hooks/form.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,8 @@
import {
createFormHook,
createFormHookContexts,
useStore,
} from '@tanstack/react-form'
import { createFormHook } from '@tanstack/react-form'
import { lazy } from 'react'
import { fieldContext, formContext, useFormContext } from './form-context.tsx'

const { fieldContext, useFieldContext, formContext, useFormContext } =
createFormHookContexts()

function TextField({ label }: { label: string }) {
const field = useFieldContext<string>()

const errors = useStore(field.store, (state) => state.meta.errors)

return (
<div>
<label>
<div>{label}</div>
<input
value={field.state.value}
onChange={(e) => field.handleChange(e.target.value)}
/>
</label>
{errors.map((error: string) => (
<div key={error} style={{ color: 'red' }}>
{error}
</div>
))}
</div>
)
}
const TextField = lazy(() => import('../components/text-fields.tsx'))

function SubscribeButton({ label }: { label: string }) {
const form = useFormContext()
Expand Down

0 comments on commit 230c2a0

Please sign in to comment.