Inertia.js Partial Reloads: An Engineering Decision for Granular Data Fetching
Inertia.js partial reloads let you refresh specific props without a full page payload. Learn how the protocol works, when to use it, and what to watch for.
01 Sept 2025, 14:59 UTC

The over-fetching problem in server-driven SPAs
Inertia.js keeps routing and authorization on the server while giving you a single-page app feel. By default, every visit or reload serializes the complete set of page props. On a dashboard that returns user, notifications, reports, and settings, a simple “refresh notifications” click can transfer kilobytes of unchanged data. The useful takeaway: you can tell the server exactly which prop keys to re-serialize, and the client will merge only that slice back into the current page.
How partial reloads work in the Inertia protocol
Partial reloads are implemented at the protocol level using two request headers:
X-Inertia-Partial-Component– the name of the currently rendered page component.X-Inertia-Partial-Data– a comma-separated list of prop keys to include (only) or exclude (except).
When the server adapter sees these headers, it filters the prop array before serialization. Official adapters such as Laravel’s Inertia::middleware and Rails’ InertiaRails::Controller handle this filtering automatically. If you use a custom or community adapter, you must inspect the headers yourself and trim the payload.
Partial reloads are available in Inertia.js v0.9 and later for the client adapters, paired with a server adapter release that understands the partial-data headers. Confirm your installed versions before relying on the behavior; older releases will simply ignore the headers and return a full payload.
Worked example: refreshing one dashboard slice
The same pattern applies across Vue, React, and Svelte because the options are passed through the shared client router. The following example uses Vue 3 and Laravel for concreteness.
Client side
<script setup>
import { router } from '@inertiajs/vue3';
function refreshNotifications() {
router.reload({
only: ['notifications'],
preserveState: true,
});
}
</script>
<template>
<button @click="refreshNotifications">Refresh notifications</button>
</template>
Omitting preserveState: true can cause the component to remount, resetting local state such as scroll position or unsaved form input. With it enabled, Inertia updates only the requested props and leaves the component instance intact.
Server side (Laravel)
public function dashboard(Request $request)
{
$partial = $request->header('X-Inertia-Partial-Data');
$wantsNotifications = $partial === null || str_contains($partial, 'notifications');
return Inertia::render('Dashboard', [
'user' => $request->user(),
'notifications' => $wantsNotifications
? $request->user()->unreadNotifications
: fn () => $request->user()->unreadNotifications,
'reports' => Report::latest()->take(5)->get(),
'settings' => $request->user()->settings,
]);
}
Note the closure for notifications: Inertia supports lazy evaluation, so the database query runs only when the prop is actually included in the response. This prevents unnecessary work even before serialization.
Trimming nested data
Server-side filtering is shallow. If reports contains a large nested chartData array, requesting only: ['reports'] still serializes everything inside it. To trim nested fields, conditionally build the prop in the controller:
'reports' => Report::latest()
->take(5)
->get()
->map(fn ($r) => [
'id' => $r->id,
'title' => $r->title,
'chartData' => $partial === null || str_contains($partial, 'reports.chartData')
? $r->chartData
: null,
]),
This example illustrates the concept; adjust the condition to match your actual partial-data string.
Verification you can run today
- Open browser DevTools and select the Network tab.
- Click the button that triggers
router.reload({ only: ['notifications'] }). - Inspect the response JSON: it should contain only the
notificationskey (plus any globally shared props). - On the server, log the incoming header. In Laravel,
Log::info($request->header('X-Inertia-Partial-Data'))writes to your configured log channel; in production, use a dedicated logging channel or debugging tool rather than leaving debug statements in code. - Trigger a full
router.visit()withoutonlyand confirm all props return, proving the fallback path works.
Limitations and edge cases
- Component match required. Partial reloads only apply when the target component name equals the current page. Navigating to a different component always triggers a full payload.
- Shared middleware data. In the Laravel adapter, props shared via
ShareInertiaDatamiddleware are merged into every response regardless of partial headers. If you need to exclude shared data from partial responses, adjust the middleware rather than the client request. - Caching and CDNs. Because the same URL can return different payloads depending on request headers, HTTP caching and CDN edge caching become harder to reason about. Authenticated partial requests are typically best kept uncached at the edge.
- Link components. The default
<Link>component does not send partial headers automatically. Recent client adapters may acceptonlyorexceptprops on<Link>, but verify your version; otherwise userouter.visit()orrouter.reload()programmatically. - Graceful degradation. If the server ignores the partial headers, the client receives a full payload and merges it normally. The app still works, but you lose the bandwidth savings.
- Request overhead. Pages with many tiny props can generate excessive requests if each interaction triggers its own partial reload. Batch related updates into a single call when possible.
Actionable next step
Pick one page that returns three or more independent data slices. Add a targeted router.reload({ only: ['sliceName'], preserveState: true }) for the most frequently refreshed slice, then measure the response size in DevTools. If the reduction is meaningful, repeat for other high-churn slices. Start small, verify the protocol behavior in your stack, and expand only where the payload savings justify the added complexity.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.