Query string persistence
This example stores the editable form draft in the URL draft parameter. It
uses the optional persistence middleware while React Hook Form continues to own
live values, validation, defaults, and form metadata.
Try the workflow
- Edit Name or Role and watch the URL after 250 ms.
- Select Save now to flush without waiting.
- Reload the page to restore the query string draft.
- Select Clear saved draft to remove the parameter without changing the current fields.
Query string draft
Edit either field. The URL draft parameter updates after 250 ms.
The adapter uses history: "replace" and shallow: true. Autosave does not add
a browser history entry for each edit and does not start a page navigation.
What this example shows
createPersistenceMiddlewareas an optionalform-please/persistencefeature;usePersistence(form, feature)for automatic restore after mount;- the hook's reactive
snapshotfor restore and save state; - a
nuqsstring state adapted to the keyed asynchronous transport.
Copy the source
"use client"
import {
,
,
} from "form-please/persistence"
import { } from "form-please/preset-native"
import { , } from "nuqs"
import { , } from "react"
import { } from "zod"
import { } from "./persistence-nuqs.js"
const = .({
: .().(1, "Enter a name"),
: .(),
})
const = .(, {
: [
{ : "text", : "field", : "Name", : "name" },
{ : "text", : "field", : "Role", : "role" },
],
})
const = .({
: "replace",
: true,
})
export function () {
const [, ] = ("draft", )
const = ()
. =
const [] = (() =>
({
: ({
: () => new (..).("draft"),
: () => .(),
}),
: "profile",
: 250,
: 1,
}),
)
const = .(, {
: { : "Ada Lovelace", : "Programmer" },
: [],
})
const = (, )
const { } =
const =
. === "conflict" || . === "failed"
return (
<
="Query string persistence preview"
="form-please-complex form-please-lab"
>
< ="form-please-lab__kicker">Query string draft</>
< ="form-please-lab__summary">
Edit either field. The URL draft parameter updates after 250 ms.
</>
<. ="form-please-lab__form" ={}>
< ="form-please-lab__actions">
<
={() => void .().(() => )}
="button"
>
Save now
</>
<
={() => void .().(() => )}
="button"
>
Clear saved draft
</>
{ && (
< ={() => .()} ="button">
Keep current form
</>
)}
</>
< ="polite">
Restore: {.}. Save: {..}.
</>
</.>
</>
)
}import type { , } from "form-please/persistence"
export type = <{
(): string | null
(: string | null): unknown | <unknown>
}>
/** Adapts a nuqs string state to the Form Please persistence transport. */
export function (
: ,
): {
return {
async () {
const = .()
if ( === null) return
return .() as
},
async () {
await .(null)
},
async (, ) {
await .(.())
},
}
}Read Form persistence for conflicts, migrations, codecs, storage failures, and lifecycle boundaries.