The reference implementation of each one lives in the library repository's
ai/guides/inertia-helpers.md. This page is the shape of the toolkit: what
exists, why, and which library component each part pairs with.
What Inertia changes
In Inertia there is no client data layer. Data arrives as page props and changes through visits. There is no store to read, no query cache to invalidate, and no fetch to await — the server re-renders and the page props change underneath you.
That model is a good fit for this library, whose components already report state rather than owning it — see Fetching data. What it lacks is a typed surface for the props themselves, a place for filters to live, and a way to await a form submit. That is what the toolkit adds, so pages stay thin.
What to install
Copying the toolkit pulls in real npm dependencies beyond your app's own
@inertiajs/vue3 and vue. Install them explicitly:
npm i @vueuse/core object-code query-string
@vueuse/core backs the keyed contexts and a couple of the UI utilities;
object-code powers the hash comparison behind isEqual; query-string backs
the three URL helpers.
Reading page props
useProp and useOptionalProp give the page's props one typed surface, so a
component reads useProp('customer') instead of reaching into usePage().props
and asserting a type at every call site.
useLazyProp and useLazyPropGetter cover Inertia's lazy props — the ones the
server only sends when asked — so a panel that loads its detail on open does not
have to hand-roll the request-and-track dance.
Filters that live in the URL
Three helpers, in increasing scope.
useRouteQuery is a single query parameter as a writable ref. Reading
tracks the current URL; writing merges the parameter and performs a replace
visit, so the server re-renders with the new value.
const status = useRouteQuery('status', 'open');
Writing the current value is a no-op, which is what makes it safe to sync from a server response without a visit loop.
useQuery and useQueryObject widen that to a parsed query string and
to a whole object of filters.
Bind these straight into the components. A BbSelect
filter and a BbTable's v-model:sort are both just
refs, and once they are URL-backed the page is shareable, restorable and
back-button-correct without any extra code:
<BbSelect v-model="status" :items="statuses" label="Status" />
<BbTable v-model:sort="sort" :items="rows" :columns="columns" />
Sharing data down a page
Keyed contexts, instead of prop drilling.
usePaginatedResponseContext publishes the Laravel paginator shape — data
plus a meta block of total, per_page, current_page and last_page — so
a toolbar, a table and a pager can each read it where they stand.
useTableSelectionContext and useTableContext do the same for a table's
selection and its own state, which is what lets a bulk-action bar outside the
table know what is selected inside it.
Forms that return promises
usePromiseForm is Inertia's useForm with promise semantics:
post, put, patch and remove resolve on success and reject on validation
errors. form is still the underlying Inertia form, so form.errors,
form.isDirty and form.reset() all work.
The payoff is that a submit becomes awaitable, which is exactly what the library's own automatic loading states expect:
<BbButton type="submit" @click="() => submit(`/customers/${id}`, 'put')">
Save
</BbButton>
BbButton shows its spinner and disables itself for
the life of that promise with nothing else written, and useConfirm's onClick
takes the same shape.
One edge worth knowing: a non-Inertia response — a 423 entity lock, a 419 CSRF
mismatch — fires neither onSuccess nor onError, only onFinish. The helper
settles the promise there too, so a failed visit releases the busy state instead
of hanging the form forever.
Guarding a dirty form
useConfirmOnRouteLeave intercepts Inertia visits and asks through
useConfirm before leaving. It needs
<BbConfirm /> mounted at the app root, like any other confirm.
Bind disabled to !form.isDirty and the guard only appears when there is
something to lose. Server redirects after a whitelisted action carry an
X-Whitelisted-Action header, so a saving visit's own redirect is never blocked
by the guard it just triggered — the failure mode this exists to prevent.
useHasChanged gives you the same dirty signal for state that is not an Inertia
form.
The rest of the toolkit
The remaining helpers are small and independent. Take the ones you need:
| Helper | For |
|---|---|
useLoading | Wrapping any async function with a loading ref |
useAsyncFn | The same, with the result and error surfaced |
useResourceContext | Sharing one resource's state across a page |
useDeleteCmd | The delete-with-confirm command, wired once |
useScreenSizes | Breakpoint state for adaptive layouts |
useIndexById | Turning a list into a keyed lookup |
useUntil | Awaiting a reactive condition |
None of them are required. The three that change how a page is written are
useRouteQuery, usePromiseForm and the paginated context — start there and add
the rest when a page asks for them.
Adopt them in that order. Put one filter in the URL, convert one submit to a promise, then introduce a context only when two separate regions need the same page data. Copying the full toolkit up front adds dependencies and abstractions before the page has a use for them.