Skip to main content
This page specifies the Inertia protocol, which defines the requests and responses exchanged between the Inertia client and your server. The how it works page gives a high-level overview. The protocol is framework-agnostic, so any backend that speaks HTTP may implement it. The official Laravel adapter is the reference implementation, and the requests and responses on this page show its output. Placeholders such as ... and comments shorten the longer examples. The building an adapter guide explains how to structure an implementation around this specification.

Request Lifecycle Diagram

The following diagram shows the request lifecycle of an Inertia application. The first visit is a standard request, and the server returns an HTML document with a root element and a script element that holds the page object. With SSR enabled, the root element also contains server-rendered markup, which the client hydrates. Later visits are XHR requests that return JSON, and Inertia swaps the page component without a full-page reload.

HTML Responses

The first request to an Inertia app is a regular full-page browser request, with no Inertia headers. The server answers it with a complete HTML document. The document loads the site’s CSS and JavaScript and contains a root <div>, where the client-side app mounts. A <script type="application/json"> element holds the JSON-encoded page object for the initial page. The script’s data-page attribute matches the root element’s id. Inertia reads the page object from this script, boots your client-side framework, and renders the initial page component. Earlier Inertia versions stored the page object in a data-page attribute on the root <div>. The current client does not read that attribute.
The serialized page object must not contain a literal </script> sequence, since it closes the script element early and breaks the page. Escaping forward slashes (/ becomes \/) or the < character (< becomes \u003C) prevents this. The Laravel adapter escapes both, so the example above contains "\/events\/80". The server must not use HTML-entity encoding here either, because the browser does not decode entities inside a script element and JSON parsing fails.

Inertia Responses

After the app boots, Inertia makes every later request with XHR and sets the X-Inertia header to true. The header marks the request as an Inertia visit rather than a full-page visit. The server detects the X-Inertia header and returns the page object as JSON instead of an HTML document.

How the Client Handles Responses

The client inspects each response before it changes the page, and handles it as follows:
  • An Inertia response (X-Inertia: true): the client applies the page object and swaps the component.
  • A 409 Conflict with X-Inertia-Location: the client makes a full window.location visit to that URL, which loads fresh assets. A background request skips this visit when the asset version changed, because a reload the user did not start would discard unsaved state.
  • A 409 Conflict with X-Inertia-Redirect: the client makes a new Inertia GET visit to that URL.
  • Any other response, such as an HTML document, plain JSON, or an error page: the client treats it as an exception. It fires a cancelable HTTP exception event and shows an error modal unless a listener cancels the event.
Standard 3xx redirects never reach these rules. The browser follows them and sends the same request headers to the new location. X-Inertia therefore reaches the redirect target, and the client inspects only the final response in the chain. The redirects section lists the statuses a server returns to control this. An Inertia response with a status of 400 or greater fires the same cancelable HTTP exception event before the client renders it. Canceling the event stops the render, so the application may handle the failure itself.

Request Headers

The Inertia client sends the following request headers.
boolean
Set to true on every Inertia request.
string
Set to XMLHttpRequest on all Inertia requests.
string
Set to text/html, application/xhtml+xml.
string
Set to application/json for requests with a JSON body. Requests without a body, such as GET requests, omit it.
string
The asset version of the current page. The client omits the header when the version is an empty string.
string
The CSRF token read from the XSRF-TOKEN cookie. The cookie and header names are configurable.
string
Set to prefetch on prefetch requests.
string
The component name for partial reloads.
string
Comma-separated list of props to include in a partial reload.
string
Comma-separated list of props to exclude from a partial reload.
string
Comma-separated list of props to reset.
string
Set to no-cache on reload requests, so caches do not serve stale content.
string
The name of the error bag for validation errors.
string
Whether the client appends or prepends the requested page of an infinite scroll prop.
string
The broadcaster’s connection id, sent on same-origin requests once the application configures live props or a custom socket ID resolver. The server may use it to skip broadcasting back to the connection that caused the change.
string
Comma-separated list of the once prop keys that the client has loaded and that have not expired. The server skips these props unless a partial reload requests them or the server refreshes them.
Precognition validation requests use the following headers.
boolean
Set to true on a Precognition validation request.
string
Comma-separated list of field names to validate.

Response Headers

The server sets the following headers on an Inertia JSON response.
boolean
Set to true on an Inertia response.
string
Set to X-Inertia, so browsers and caches keep the HTML response and the JSON response for one URL apart.
The following headers appear on 409 Conflict control responses. A 409 response tells the client to navigate rather than render a page, so it carries no X-Inertia header.
string
The destination URL for a full window.location visit. Set on an external redirect and on an asset version mismatch.
string
The full redirect URL, including its fragment, for a redirect whose target contains a URL fragment. The client makes a new Inertia GET visit instead of a full-page reload.
string
The server’s current asset version, set on a version mismatch 409.
Precognition validation responses use the following headers.
string
Set to true on a Precognition validation response.
string
Set to true, with a 204 No Content status, when validation passes.
string
Set to Precognition on every response that passes through the Precognition middleware.

The Page Object

The server and the client exchange data through the page object. The page object holds what the client needs to render the page component, update the browser’s history state, and track the site’s asset version. It may contain the following properties.
string
The name of the JavaScript page component.
object
The page props. Usually includes an errors object.
string
The page URL.
string
The current asset version.
boolean
Whether the client encrypts the page’s history state. Included only when true.
boolean
Whether the client clears encrypted history state. Included only when true.
boolean
Whether the props wrap integers outside JavaScript’s safe range as {"$bigint": "<digits>"} markers. The client turns each marker into a BigInt value. Included only when true.
boolean
Whether the client keeps the URL fragment of the original request after a redirect. Included only when true.
array
Prop keys whose new values the client appends to the existing values.
array
Prop keys whose new values the client prepends to the existing values.
array
Prop keys whose new values the client deep merges into the existing values.
array
The fields that identify items when the client merges props.
object
The pagination state of each infinite scroll prop.
object
The deferred props that the client loads in follow-up requests, grouped by request group.
array
The keys of deferred props that threw and that the server rescued. The client renders the rescue slot of the <Deferred> component for these props.
array
The top-level keys of the shared props. The client carries these props over during instant visits.
object
The once props of the page. Each entry maps a key to an object with the prop name and an expiresAt timestamp in milliseconds, or null for no expiry.
object
The live props of the page. Each entry maps a prop path to the channels and events that refresh it, and an optional throttle in milliseconds.
object
The flash data for the current request. Included only when flash data exists. The client exposes it through the inertia:flash event and leaves it out of the saved history state.
Every page object contains component, props, url, and version. A minimal page object looks like this.
The server emits the other fields only when they apply. The client reads a missing field as an empty array, an empty object, or false. A server may therefore omit an empty field or send it, with the same result.

Prop Evaluation Model

Every prop belongs to one or more categories. The category decides whether the server resolves the prop for a request, and which metadata fields describe it. These rules apply only on the server. The client applies whatever metadata it receives. Resolution differs between the two request modes. A full visit resolves every eligible prop. A partial reload resolves only the props that the client requests for the same page component. Full visit Partial reload Some metadata fields depend on the mode:
  • deferredProps appears only on full visits. Deferred props resolve in the follow-up partial reload, so deferredProps is empty or absent there.
  • rescuedProps appears only on partial reloads. It lists the deferred props that failed to resolve.
  • Merge, scroll, once, and live metadata appears on each response that contains props with those behaviors, so a partial reload keeps the live listeners of the props it refreshes up to date.

Always Props

The server resolves always props on every response in both modes. Always props ignore the partial reload filters, so the server sends an always prop even when the request lists it in except. Always props carry no metadata. The Laravel adapter’s middleware shares errors as an always prop, so its page objects include an errors object unless a page prop overrides it.

Composing Categories

A prop may belong to several categories, such as deferred and mergeable, once and deferred, or scroll and deferred. The metadata fields are independent. The page object carries every label that applies, subject to the mode, the partial reload filters, and the reset rules.

Partial Reloads

A partial reload requests a subset of the props on a later visit to the same page component. The partial reloads page covers the client-side API. A partial reload request carries the X-Inertia-Partial-Component header. It may also carry X-Inertia-Partial-Data, X-Inertia-Partial-Except, or both.
  • X-Inertia-Partial-Data lists the prop keys to return.
  • X-Inertia-Partial-Except lists the prop keys to leave out. Sent alone, it returns every prop except the listed ones.
  • With both headers, the server first narrows the response to the X-Inertia-Partial-Data list, then removes the X-Inertia-Partial-Except list. A prop named in both headers is left out.
Entries in both headers may be dot-notated paths, such as auth.user, which select or exclude a nested prop. The Laravel adapter applies nested paths only inside plain arrays, and returns a prop that a closure resolves in full. X-Inertia-Partial-Component holds the name of the page component being reloaded. Partial reloads apply only to the same page component. The server resolves a request that ends on a different component as a full visit. A login page after the session expired is a common example.

Resetting Props

The X-Inertia-Reset header lists the prop paths that the client resets before it applies new data. The client sends each reset path in both X-Inertia-Reset and X-Inertia-Partial-Data. The server resolves a reset prop again and returns it in props without a merge label, so the client replaces the value instead of merging it. For a scroll prop, the server also sets scrollProps[path].reset. The infinite scroll component then resets its pagination state to the returned page. The resetting props section covers the client-side API.

Deferred and Optional Props

A deferred prop is data that the client loads in a follow-up request. On a full visit, the server skips the prop and lists its key under deferredProps, grouped by request group. Group names are arbitrary, and the client sends one partial reload per group. See the deferred props documentation for the server and client APIs.
Optional props are never announced. The server skips them on full visits and resolves them only when a partial reload selects them with only or except.

Rescued Deferred Props

A rescued deferred prop that throws does not fail the response, which is still a 200 with every other prop. The server reports the exception through the framework’s error handling and leaves the prop out of props entirely, rather than sending it as null. Instead, it adds the key to the top-level rescuedProps array, and the client renders the rescue slot of the <Deferred> component for it. The deferred props error handling documentation covers the server and client APIs.

Merging Props

A merge prop tells the client to combine new data with the data already on the page instead of replacing it. The client appends or prepends arrays and merges objects, either shallowly or deeply. The page object labels each merge prop under mergeProps to append, prependProps to prepend, or deepMergeProps to deep merge. The client merges only on partial reloads, so a full visit replaces the prop even when it carries a merge label. See the merging props documentation for more details.

Matching Items

The matchPropsOn array names the field that identifies each item. The client uses that field to update an existing item in place instead of adding a duplicate. Each entry has the form "<propPath>.<keyField>", and the client splits it at the last dot. The text before the last dot is the prop path, and the final segment is the key field. For example, conversations.data.id matches items of the array at conversations.data by their id field. The matching items section covers the server-side API.

Once Props

The server resolves a once prop on its first use. The client remembers the value and reuses it on later pages that include the same prop, until the value expires or the server refreshes it. The page object carries an onceProps object. Each entry maps a key to the prop name and an expiration timestamp in milliseconds, or null for no expiry. The once props page covers the server and client APIs.
On later Inertia requests, the client sends the keys of loaded once props that have not expired in the X-Inertia-Except-Once-Props header. The server skips those props and leaves them out of the response, except in the cases that force-fresh and partial reloads describes. The client reuses the values it already has.
The response lists plans in onceProps but not in props, because the client already has the value. The onceProps key identifies the once prop across pages, and prop holds the prop name. The two differ when the application uses custom keys.

Force-Fresh and Partial Reloads

The server may send a new value for a once prop even when X-Inertia-Except-Once-Props lists its key, for example after the underlying data changes. The new value and its expiresAt replace the client’s copy. The server ignores the except-once header on a partial reload, so it always resolves a requested once prop.

Live Props

A live prop refreshes itself whenever a broadcast event arrives. The page object carries a liveProps entry for each live prop, listing the channels it listens on, the events broadcast on each channel, and an optional throttle in milliseconds. Channel names are unprefixed and carry their type separately, so the client applies whichever prefix its broadcaster expects. The live props page covers the server and client APIs.
An incoming event refreshes the props that listen for it with a regular partial reload, so the client receives whatever the server resolves at that moment. An event may instead carry new values under an __inertia.props key in its payload, keyed by prop path, and the client writes them into the page without a request. The client ignores values for props that don’t listen for the event. It also sends the broadcaster’s connection id in the X-Socket-Id header, so the server may exclude the connection that caused the change.

Infinite Scroll

Infinite scroll builds on merge props. The server labels the prop’s inner array for merging, usually at <path>.data, and the page object carries a scrollProps entry that describes the pagination state. See the infinite scroll documentation for the server and client APIs.
Every scroll response repeats the merge label and sends the current pagination state. On a reset request, the server also sets scrollProps[path].reset to true, and the client resets its pagination state to the returned page. A deferred scroll prop emits no scrollProps on the full visit, because it resolves in the follow-up request.

Asset Versioning

The version field of the page object identifies the current version of the site’s assets. It may be any string that changes when the assets change, such as a file hash. A server that does not track asset versions sends an empty string. The client sends a non-empty version back in the X-Inertia-Version header. The server compares the two values, usually in middleware, and treats a missing header as an empty string. Matching versions let the request continue. On a GET request with a mismatch, the server returns a 409 Conflict. The X-Inertia-Location header holds the full URL of the request, and X-Inertia-Version holds the server’s current version. For a visit the user started, the client then makes a full-page visit to that URL, which follows any server-side redirect to the final page. For a background request, the client keeps the current page, as described in how the client handles responses. A non-GET request never receives a mismatch 409, but the GET redirect that follows it may. On a version mismatch, the server also keeps the session’s flash data for the next request.
The asset versioning page covers how to configure the version and how the client handles background requests.

Redirects

Inertia uses standard HTTP redirects for navigation after a server-side action. The server converts a 302 Found after a PUT, PATCH, DELETE, or QUERY request into a 303 See Other. The browser then requests the redirect target with GET instead of repeating the original method. An external redirect points outside the Inertia app, where the Inertia request headers do not carry over, so an XHR visit may not follow it. The server returns a 409 Conflict with an X-Inertia-Location header instead, and the client makes a full window.location visit. For a redirect whose target contains a URL fragment, the server returns a 409 Conflict with the full URL in an X-Inertia-Redirect header, and the client makes a new Inertia GET visit to that URL. Prefetch requests are the exception and receive the redirect unchanged. This is separate from the preserveFragment page object field, which keeps the fragment of the original request after an ordinary redirect. The redirects page covers the server-side API.

Validation Errors

The server sends validation errors in the errors prop. The Laravel adapter’s middleware registers errors as an always prop. Every page object rendered through the middleware therefore contains errors, unless a page prop overrides it. The value is {} when there are no errors. The client treats a missing errors prop as an empty object. Each key maps to either a single message or an array of messages, and the client renders the shape it receives. The Laravel adapter sends the first message per field by default and may be configured to send all messages. A request may name an error bag in the X-Inertia-Error-Bag header, so multiple forms on one page keep their errors apart. The Laravel adapter handles error bags by these rules:
  • A request with the X-Inertia-Error-Bag header receives the default bag’s errors under the requested name.
  • A request without the header receives the default bag unwrapped.
  • Named bags appear under their own names only when no default bag exists, with or without the header.
The validation page covers the client-side API.

HTTP Status Codes

Inertia uses the following HTTP status codes. Precognition validation requests use the following status codes.

Server-Side Rendering

Inertia does not render page components on the server by default. SSR adds a second contract, between the server-side adapter and Inertia’s Node SSR server. The adapter sends the page object to the SSR server in a POST request, and the SSR server returns the rendered markup for the HTML document.
The response fills the two slots of the root template. The Laravel adapter provides these slots as the <x-inertia::head> and <x-inertia::app> Blade components. The head array holds the elements for the document’s <head>, such as the title and meta tags. The body string replaces the page object script and the root <div> of a document rendered without SSR, because the SSR markup already contains both. Its root element carries data-server-rendered="true", so the client hydrates the existing DOM instead of mounting a new app. The SSR server escapes the embedded page object, so the adapter prints the body markup unchanged. During development, the Vite plugin accepts the same render request at a /__inertia_ssr endpoint on the Vite dev server, so no separate Node process runs. The plugin registers the endpoint only when it finds an SSR entry point. The Laravel adapter sends its render requests there while Vite runs in hot mode. The SSR server has two more endpoints. A GET request to /health returns {"status":"OK","timestamp":...}. An adapter may call it to check that the process runs before it sends a render. A request to /shutdown stops the process. The Laravel adapter’s inertia:stop-ssr command uses this endpoint. Error formatting is enabled by default. With it enabled, a failed render returns a 500 response with a JSON body that classifies the error.
The response always contains error, hint, and timestamp. It contains component, url, stack, and sourceLocation when the SSR server can determine them. The type field names the cause:
  • browser-api: the render used a browser global. browserApi names the global.
  • component-resolution: the SSR server could not resolve the page component.
  • render: a component threw any other error.
A failed render is not fatal to the request. The adapter reports the error through the application’s error handling and returns the document without SSR markup. A broken SSR build therefore does not take the site down. The Laravel adapter throws an exception instead when its inertia.ssr.throw_on_error option is enabled, which is useful in end-to-end tests. The Laravel adapter dispatches an SsrRenderFailed event. It also reports a connection type when it cannot reach the SSR server.