├── .gitignore ├── images └── pingcrm.png ├── mp4 ├── csrf-mismatch-modal.mp4 └── csrf-mismatch-warning.mp4 ├── fonts └── inter │ ├── InterVariable.woff2 │ └── InterVariable-Italic.woff2 ├── snippets ├── docs.css ├── client-specific.jsx ├── docs.js ├── vue-specific.jsx ├── react-specific.jsx ├── vue2-specific.jsx ├── vue3-specific.jsx ├── svelte-specific.jsx ├── svelte4-specific.jsx ├── svelte5-specific.jsx ├── current-code-tab.jsx └── color-generator.jsx ├── favicon.svg ├── README.md ├── v2 ├── security │ ├── authentication.mdx │ ├── authorization.mdx │ ├── history-encryption.mdx │ └── csrf-protection.mdx ├── getting-started │ ├── index.mdx │ ├── demo-application.mdx │ └── upgrade-guide.mdx ├── core-concepts │ ├── who-is-it-for.mdx │ └── how-it-works.mdx ├── installation │ ├── community-adapters.mdx │ └── server-side-setup.mdx ├── the-basics │ ├── redirects.mdx │ ├── routing.mdx │ ├── file-uploads.mdx │ └── title-and-meta.mdx ├── advanced │ ├── asset-versioning.mdx │ ├── code-splitting.mdx │ ├── scroll-management.mdx │ └── error-handling.mdx └── data-props │ ├── polling.mdx │ ├── shared-data.mdx │ ├── once-props.mdx │ ├── deferred-props.mdx │ ├── merging-props.mdx │ ├── remembering-state.mdx │ ├── partial-reloads.mdx │ └── flash-data.mdx ├── style.css ├── assets └── svelte.svg ├── v1 ├── advanced │ ├── authentication.mdx │ ├── authorization.mdx │ ├── asset-versioning.mdx │ ├── scroll-management.mdx │ ├── csrf-protection.mdx │ ├── error-handling.mdx │ ├── testing.mdx │ ├── partial-reloads.mdx │ └── remembering-state.mdx ├── getting-started │ ├── index.mdx │ └── demo-application.mdx ├── core-concepts │ ├── who-is-it-for.mdx │ ├── how-it-works.mdx │ └── the-protocol.mdx ├── the-basics │ ├── responses.mdx │ ├── redirects.mdx │ ├── routing.mdx │ ├── file-uploads.mdx │ └── shared-data.mdx └── installation │ ├── server-side-setup.mdx │ └── client-side-setup.mdx ├── LICENSE └── logo ├── dark.svg └── light.svg /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /images/pingcrm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yilanboy/docs/main/images/pingcrm.png -------------------------------------------------------------------------------- /mp4/csrf-mismatch-modal.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yilanboy/docs/main/mp4/csrf-mismatch-modal.mp4 -------------------------------------------------------------------------------- /mp4/csrf-mismatch-warning.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yilanboy/docs/main/mp4/csrf-mismatch-warning.mp4 -------------------------------------------------------------------------------- /fonts/inter/InterVariable.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yilanboy/docs/main/fonts/inter/InterVariable.woff2 -------------------------------------------------------------------------------- /snippets/docs.css: -------------------------------------------------------------------------------- 1 | svg[style*="regular/s.svg"] { 2 | mask-image: url("/docs/assets/svelte.svg") !important; 3 | } 4 | -------------------------------------------------------------------------------- /fonts/inter/InterVariable-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yilanboy/docs/main/fonts/inter/InterVariable-Italic.woff2 -------------------------------------------------------------------------------- /snippets/client-specific.jsx: -------------------------------------------------------------------------------- 1 | export const ClientSpecific = ({ children }) => { 2 | // What's this you ask? It's me getting around some mintlify wildness 3 | // I don't know why this works, but it does. Mintlify you crazy. 4 | const [nada, setNada] = useState(); 5 | 6 | return children; 7 | }; 8 | -------------------------------------------------------------------------------- /snippets/docs.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const originalSetItem = localStorage.setItem; 3 | 4 | localStorage.setItem = function (key, value) { 5 | const event = new CustomEvent("localStorageUpdate", { 6 | detail: { key, value }, 7 | }); 8 | 9 | window.dispatchEvent(event); 10 | 11 | originalSetItem.call(this, key, value); 12 | }; 13 | })(); 14 | -------------------------------------------------------------------------------- /favicon.svg: -------------------------------------------------------------------------------- 1 | 8 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Development 2 | 3 | Install the [Mintlify CLI](https://www.npmjs.com/package/mint) to preview your documentation changes locally. To install, use the following command: 4 | 5 | ``` 6 | npm i -g mint 7 | ``` 8 | 9 | Run the following command at the root of your documentation, where your `docs.json` is located: 10 | 11 | ``` 12 | mint dev 13 | ``` 14 | 15 | View your local preview at `http://localhost:3000`. 16 | 17 | ### Resources 18 | 19 | - [Mintlify documentation](https://mintlify.com/docs) 20 | -------------------------------------------------------------------------------- /snippets/vue-specific.jsx: -------------------------------------------------------------------------------- 1 | export const VueSpecific = ({ children }) => { 2 | const [code, setCode] = useState( 3 | localStorage.getItem("code")?.replace(/"/g, "") || null, 4 | ); 5 | 6 | const callback = useCallback((event) => { 7 | if (event.detail.key === "code") { 8 | setCode(event.detail.value.replace(/"/g, "")); 9 | } 10 | }, []); 11 | 12 | useEffect(() => { 13 | window.addEventListener("storage", callback); 14 | window.addEventListener("localStorageUpdate", callback); 15 | 16 | return () => { 17 | window.removeEventListener("storage", callback); 18 | window.removeEventListener("localStorageUpdate", callback); 19 | }; 20 | }); 21 | 22 | if (code !== "Vue") { 23 | return null; 24 | } 25 | 26 | return children; 27 | }; 28 | -------------------------------------------------------------------------------- /snippets/react-specific.jsx: -------------------------------------------------------------------------------- 1 | export const ReactSpecific = ({ children }) => { 2 | const [code, setCode] = useState( 3 | localStorage.getItem("code")?.replace(/"/g, "") || null, 4 | ); 5 | 6 | const callback = useCallback((event) => { 7 | if (event.detail.key === "code") { 8 | setCode(event.detail.value.replace(/"/g, "")); 9 | } 10 | }, []); 11 | 12 | useEffect(() => { 13 | window.addEventListener("storage", callback); 14 | window.addEventListener("localStorageUpdate", callback); 15 | 16 | return () => { 17 | window.removeEventListener("storage", callback); 18 | window.removeEventListener("localStorageUpdate", callback); 19 | }; 20 | }); 21 | 22 | if (code !== "React") { 23 | return null; 24 | } 25 | 26 | return children; 27 | }; 28 | -------------------------------------------------------------------------------- /snippets/vue2-specific.jsx: -------------------------------------------------------------------------------- 1 | export const Vue2Specific = ({ children }) => { 2 | const [code, setCode] = useState( 3 | localStorage.getItem("code")?.replace(/"/g, "") || null, 4 | ); 5 | 6 | const callback = useCallback((event) => { 7 | if (event.detail.key === "code") { 8 | setCode(event.detail.value.replace(/"/g, "")); 9 | } 10 | }, []); 11 | 12 | useEffect(() => { 13 | window.addEventListener("storage", callback); 14 | window.addEventListener("localStorageUpdate", callback); 15 | 16 | return () => { 17 | window.removeEventListener("storage", callback); 18 | window.removeEventListener("localStorageUpdate", callback); 19 | }; 20 | }); 21 | 22 | if (code !== "Vue 2") { 23 | return null; 24 | } 25 | 26 | return children; 27 | }; 28 | -------------------------------------------------------------------------------- /snippets/vue3-specific.jsx: -------------------------------------------------------------------------------- 1 | export const Vue3Specific = ({ children }) => { 2 | const [code, setCode] = useState( 3 | localStorage.getItem("code")?.replace(/"/g, "") || null, 4 | ); 5 | 6 | const callback = useCallback((event) => { 7 | if (event.detail.key === "code") { 8 | setCode(event.detail.value.replace(/"/g, "")); 9 | } 10 | }, []); 11 | 12 | useEffect(() => { 13 | window.addEventListener("storage", callback); 14 | window.addEventListener("localStorageUpdate", callback); 15 | 16 | return () => { 17 | window.removeEventListener("storage", callback); 18 | window.removeEventListener("localStorageUpdate", callback); 19 | }; 20 | }); 21 | 22 | if (code !== "Vue 3") { 23 | return null; 24 | } 25 | 26 | return children; 27 | }; 28 | -------------------------------------------------------------------------------- /snippets/svelte-specific.jsx: -------------------------------------------------------------------------------- 1 | export const SvelteSpecific = ({ children }) => { 2 | const [code, setCode] = useState( 3 | localStorage.getItem("code")?.replace(/"/g, "") || null, 4 | ); 5 | 6 | const callback = useCallback((event) => { 7 | if (event.detail.key === "code") { 8 | setCode(event.detail.value.replace(/"/g, "")); 9 | } 10 | }, []); 11 | 12 | useEffect(() => { 13 | window.addEventListener("storage", callback); 14 | window.addEventListener("localStorageUpdate", callback); 15 | 16 | return () => { 17 | window.removeEventListener("storage", callback); 18 | window.removeEventListener("localStorageUpdate", callback); 19 | }; 20 | }); 21 | 22 | if (!code?.includes("Svelte")) { 23 | return null; 24 | } 25 | 26 | return children; 27 | }; 28 | -------------------------------------------------------------------------------- /snippets/svelte4-specific.jsx: -------------------------------------------------------------------------------- 1 | export const Svelte4Specific = ({ children }) => { 2 | const [code, setCode] = useState( 3 | localStorage.getItem("code")?.replace(/"/g, "") || null, 4 | ); 5 | 6 | const callback = useCallback((event) => { 7 | if (event.detail.key === "code") { 8 | setCode(event.detail.value.replace(/"/g, "")); 9 | } 10 | }, []); 11 | 12 | useEffect(() => { 13 | window.addEventListener("storage", callback); 14 | window.addEventListener("localStorageUpdate", callback); 15 | 16 | return () => { 17 | window.removeEventListener("storage", callback); 18 | window.removeEventListener("localStorageUpdate", callback); 19 | }; 20 | }); 21 | 22 | if (code !== "Svelte 4") { 23 | return null; 24 | } 25 | 26 | return children; 27 | }; 28 | -------------------------------------------------------------------------------- /snippets/svelte5-specific.jsx: -------------------------------------------------------------------------------- 1 | export const Svelte5Specific = ({ children }) => { 2 | const [code, setCode] = useState( 3 | localStorage.getItem("code")?.replace(/"/g, "") || null, 4 | ); 5 | 6 | const callback = useCallback((event) => { 7 | if (event.detail.key === "code") { 8 | setCode(event.detail.value.replace(/"/g, "")); 9 | } 10 | }, []); 11 | 12 | useEffect(() => { 13 | window.addEventListener("storage", callback); 14 | window.addEventListener("localStorageUpdate", callback); 15 | 16 | return () => { 17 | window.removeEventListener("storage", callback); 18 | window.removeEventListener("localStorageUpdate", callback); 19 | }; 20 | }); 21 | 22 | if (code !== "Svelte 5") { 23 | return null; 24 | } 25 | 26 | return children; 27 | }; 28 | -------------------------------------------------------------------------------- /snippets/current-code-tab.jsx: -------------------------------------------------------------------------------- 1 | export const CurrentCodeTab = () => { 2 | const [code, setCode] = useState(() => { 3 | if (typeof window === "undefined") return null; 4 | return localStorage.getItem("code")?.replace(/"/g, "") || null; 5 | }); 6 | 7 | const callback = useCallback((event) => { 8 | if (event.detail.key === "code") { 9 | setCode(event.detail.value.replace(/"/g, "")); 10 | } 11 | }, []); 12 | 13 | useEffect(() => { 14 | window.addEventListener("storage", callback); 15 | window.addEventListener("localStorageUpdate", callback); 16 | 17 | return () => { 18 | window.removeEventListener("storage", callback); 19 | window.removeEventListener("localStorageUpdate", callback); 20 | }; 21 | }); 22 | 23 | if (code?.includes("Svelte")) { 24 | return "Svelte"; 25 | } 26 | 27 | return code; 28 | }; 29 | -------------------------------------------------------------------------------- /v2/security/authentication.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Authentication 3 | --- 4 | 5 | One of the benefits of using Inertia is that you don't need a special authentication system such as OAuth to connect to your data provider (API). Also, since your data is provided via your controllers, and housed on the same domain as your JavaScript components, you don't have to worry about setting up CORS. 6 | 7 | Rather, when using Inertia, you can simply use whatever authentication system your server-side framework ships with. Typically, this will be a session based authentication system such as the authentication system included with Laravel. 8 | 9 | 16 | Laravel's starter kits provide out-of-the-box scaffolding for new Inertia applications, including authentication. 17 | 18 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Instrument+Sans:wght@400..500&display=swap"); 2 | @import url("https://fonts.googleapis.com/css2?family=Geist+Mono:wght@300..600&display=swap"); 3 | 4 | body { 5 | font-family: "Instrument Sans"; 6 | font-weight: 400; 7 | } 8 | 9 | h1, 10 | h2, 11 | h3, 12 | h4 { 13 | font-weight: 500 !important; 14 | letter-spacing: -0.025rem !important; 15 | } 16 | 17 | code, 18 | pre { 19 | font-family: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, 20 | Consolas, "Liberation Mono", "Courier New", monospace; 21 | } 22 | 23 | b, 24 | strong, 25 | .font-semibold { 26 | font-weight: 500 !important; 27 | } 28 | 29 | .prose :where(a):not(:where([class~="not-prose"], [class~="not-prose"] *)) { 30 | font-weight: 500 !important; 31 | color: rgb(var(--primary-light)) !important; 32 | } 33 | 34 | .codeblock-dark div:not(:last-child) { 35 | color: #fafafa; 36 | } 37 | -------------------------------------------------------------------------------- /assets/svelte.svg: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /v1/advanced/authentication.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Authentication 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | One of the benefits of using Inertia is that you don't need a special authentication system such as OAuth to connect to your data provider (API). Also, since your data is provided via your controllers, and housed on the same domain as your JavaScript components, you don't have to worry about setting up CORS. 8 | 9 | Rather, when using Inertia, you can simply use whatever authentication system your server-side framework ships with. Typically, this will be a session based authentication system such as the authentication system included with Laravel. 10 | 11 | 18 | Laravel's starter kits provide out-of-the-box scaffolding for new Inertia applications, including authentication. 19 | 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Mintlify 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /v2/security/authorization.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Authorization 3 | --- 4 | 5 | When using Inertia, authorization is best handled server-side in your application's authorization policies. However, you may be wondering how to perform checks against your authorization policies from within your Inertia page components since you won't have access to your framework's server-side helpers. 6 | 7 | The simplest approach to solving this problem is to pass the results of your authorization checks as props to your page components. 8 | 9 | ```php 10 | class UsersController extends Controller 11 | { 12 | public function index() 13 | { 14 | return Inertia::render('Users/Index', [ 15 | 'can' => [ 16 | 'create_user' => Auth::user()->can('create', User::class), 17 | ], 18 | 'users' => User::all()->map(function ($user) { 19 | return [ 20 | 'first_name' => $user->first_name, 21 | 'last_name' => $user->last_name, 22 | 'email' => $user->email, 23 | 'can' => [ 24 | 'edit_user' => Auth::user()->can('edit', $user), 25 | ] 26 | ]; 27 | }), 28 | ]); 29 | } 30 | } 31 | ``` -------------------------------------------------------------------------------- /v2/getting-started/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | --- 4 | 5 | Inertia is a new approach to building classic server-driven web apps. We call it the modern monolith. 6 | 7 | Inertia allows you to create fully client-side rendered, single-page apps, without the complexity that comes with modern SPAs. It does this by leveraging existing server-side patterns that you already love. 8 | 9 | Inertia has no client-side routing, nor does it require an API. Simply build controllers and page views like you've always done! Inertia works great with any backend framework, but it's fine-tuned for [Laravel](https://laravel.com). 10 | 11 | ## Not a Framework 12 | 13 | Inertia isn't a framework, nor is it a replacement for your existing server-side or client-side frameworks. Rather, it's designed to work with them. Think of Inertia as glue that connects the two. Inertia does this via adapters. We currently have three official client-side adapters (React, Vue, and Svelte) and three server-side adapters (Laravel, Rails, and Phoenix). 14 | 15 | ## Next Steps 16 | 17 | Want to learn a bit more before diving in? Check out the [who is it for](/v2/core-concepts/who-is-it-for) and [how it works](/v2/core-concepts/how-it-works) pages. Or, if you're ready to get started, jump right into the [installation instructions](/v2/installation/server-side-setup). -------------------------------------------------------------------------------- /v1/advanced/authorization.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Authorization 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | When using Inertia, authorization is best handled server-side in your application's authorization policies. However, you may be wondering how to perform checks against your authorization policies from within your Inertia page components since you won't have access to your framework's server-side helpers. 8 | 9 | The simplest approach to solving this problem is to pass the results of your authorization checks as props to your page components. 10 | 11 | ```php 12 | class UsersController extends Controller 13 | { 14 | public function index() 15 | { 16 | return Inertia::render('Users/Index', [ 17 | 'can' => [ 18 | 'create_user' => Auth::user()->can('create', User::class), 19 | ], 20 | 'users' => User::all()->map(function ($user) { 21 | return [ 22 | 'first_name' => $user->first_name, 23 | 'last_name' => $user->last_name, 24 | 'email' => $user->email, 25 | 'can' => [ 26 | 'edit_user' => Auth::user()->can('edit', $user), 27 | ] 28 | ]; 29 | }), 30 | ]); 31 | } 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /logo/dark.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /logo/light.svg: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /v1/getting-started/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Introduction 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | Inertia is a new approach to building classic server-driven web apps. We call it the modern monolith. 8 | 9 | Inertia allows you to create fully client-side rendered, single-page apps, without the complexity that comes with modern SPAs. It does this by leveraging existing server-side patterns that you already love. 10 | 11 | Inertia has no client-side routing, nor does it require an API. Simply build controllers and page views like you've always done! Inertia works great with any backend framework, but it's fine-tuned for [Laravel](https://laravel.com). 12 | 13 | ## Not a Framework 14 | 15 | Inertia isn't a framework, nor is it a replacement for your existing server-side or client-side frameworks. Rather, it's designed to work with them. Think of Inertia as glue that connects the two. Inertia does this via adapters. We currently have three official client-side adapters (React, Vue, and Svelte) and three server-side adapters (Laravel, Rails, and Phoenix). 16 | 17 | ## Next Steps 18 | 19 | Want to learn a bit more before diving in? Check out the [who is it for](/v1/core-concepts/who-is-it-for) and [how it works](/v1/core-concepts/how-it-works) pages. Or, if you're ready to get started, jump right into the [installation instructions](/v1/installation/server-side-setup). 20 | -------------------------------------------------------------------------------- /v2/core-concepts/who-is-it-for.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Who Is Inertia.js For? 3 | --- 4 | 5 | Inertia was crafted for development teams and solo hackers who typically build server-side rendered applications using frameworks like Laravel, Ruby on Rails, Django, or Phoenix. You're used to creating controllers, retrieving data from the database (via an ORM), and rendering views. 6 | 7 | But what happens when you want to replace your server-side rendered views with a modern, JavaScript-based single-page application frontend? The answer is always "you need to build an API". Because that's how modern SPAs are built. 8 | 9 | This means building a REST or GraphQL API. It means figuring out authentication and authorization for that API. It means client-side state management. It means setting up a new Git repository. It means a more complicated deployment strategy. And this list goes on. It's a complete paradigm shift, and often a complete mess. We think there is a better way. 10 | 11 | > **Inertia empowers you to build a modern, JavaScript-based single-page application without the tiresome complexity.** 12 | 13 | Inertia works just like a classic server-side rendered application. You create controllers, you get data from the database (via your ORM), and you render views. But, Inertia views are JavaScript page components written in React, Vue, or Svelte. 14 | 15 | This means you get all the power of a client-side application and modern SPA experience, but you don't need to build an API. We think it's a breath of fresh air that will supercharge your productivity. 16 | -------------------------------------------------------------------------------- /v1/getting-started/demo-application.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Demo Application 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | We've setup a demo app for Inertia.js called [Ping CRM](https://demo.inertiajs.com). This application is built using Laravel and Vue. You can find the source code on [GitHub](https://github.com/inertiajs/pingcrm). 8 | 9 | The Ping CRM demo is hosted on Heroku and the database is reset every hour. Please be respectful when editing data. 10 | 11 | [![Ping CRM](/images/pingcrm.png)](https://demo.inertiajs.com)In addition to the Vue version of Ping CRM, we also maintain a Svelte version of the application, which you can find [on GitHub](https://github.com/inertiajs/pingcrm-svelte). 12 | 13 | ## Third Party 14 | 15 | Beyond our official demo app, Ping CRM has also been translated into numerous different languages and frameworks. 16 | 17 | - [Ruby on Rails/Vue](https://github.com/ledermann/pingcrm/) by Georg Ledermann 18 | - [Laravel/React](https://github.com/Landish/pingcrm-react) by Lado Lomidze 19 | - [Laravel/Svelte](https://github.com/zgabievi/pingcrm-svelte) by Zura Gabievi 20 | - [Laravel/Mithril.js](https://github.com/tbreuss/pingcrm-mithril) by Thomas Breuss 21 | - [Yii 2/Vue](https://github.com/tbreuss/pingcrm-yii2) by Thomas Breuss 22 | - [Symfony/Vue](https://github.com/aleksblendwerk/pingcrm-symfony) by Aleks Seltenreich 23 | - [Clojure/React](https://github.com/prestancedesign/pingcrm-clojure) by Michaël Salihi 24 | -------------------------------------------------------------------------------- /v1/core-concepts/who-is-it-for.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Who Is Inertia.js For? 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | Inertia was crafted for development teams and solo hackers who typically build server-side rendered applications using frameworks like Laravel, Ruby on Rails, Django, or Phoenix. You're used to creating controllers, retrieving data from the database (via an ORM), and rendering views. 8 | 9 | But what happens when you want to replace your server-side rendered views with a modern, JavaScript-based single-page application frontend? The answer is always "you need to build an API". Because that's how modern SPAs are built. 10 | 11 | This means building a REST or GraphQL API. It means figuring out authentication and authorization for that API. It means client-side state management. It means setting up a new Git repository. It means a more complicated deployment strategy. And this list goes on. It's a complete paradigm shift, and often a complete mess. We think there is a better way. 12 | 13 | **Inertia empowers you to build a modern, JavaScript-based single-page application without the tiresome complexity.** 14 | 15 | Inertia works just like a classic server-side rendered application. You create controllers, you get data from the database (via your ORM), and you render views. But, Inertia views are JavaScript page components written in React, Vue, or Svelte. 16 | 17 | This means you get all the power of a client-side application and modern SPA experience, but you don't need to build an API. We think it's a breath of fresh air that will supercharge your productivity. 18 | -------------------------------------------------------------------------------- /v1/the-basics/responses.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Responses 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | ## Creating Responses 8 | 9 | Creating an Inertia response is simple. To get started, invoke the `Inertia::render()` method within your controller or route, providing both the name of the [JavaScript page component](/v1/the-basics/pages) that you wish to render, as well as any props (data) for the page. 10 | 11 | In the example below, we will pass a single prop (`event`) which contains four attributes (`id`, `title`, `start_date` and `description`) to the `Event/Show` page component. 12 | 13 | ```php 14 | use Inertia\Inertia; 15 | 16 | class EventsController extends Controller 17 | { 18 | public function show(Event $event) 19 | { 20 | return Inertia::render('Event/Show', [ 21 | 'event' => $event->only( 22 | 'id', 23 | 'title', 24 | 'start_date', 25 | 'description' 26 | ), 27 | ]); 28 | 29 | // Alternatively, you can use the inertia() helper... 30 | return inertia('Event/Show', [ 31 | 'event' => $event->only( 32 | 'id', 33 | 'title', 34 | 'start_date', 35 | 'description' 36 | ), 37 | ]); 38 | } 39 | } 40 | ``` 41 | 42 | ```html 43 | 44 | ``` 45 | 46 | ```php 47 | return Inertia::render('Event', ['event' => $event]) 48 | ->withViewData(['meta' => $event->meta]); 49 | ``` 50 | 51 | ```html 52 | 53 | ``` 54 | -------------------------------------------------------------------------------- /v2/getting-started/demo-application.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Demo Application 3 | --- 4 | 5 | We've setup a demo app for Inertia.js called [Ping CRM](https://demo.inertiajs.com). This application is built using Laravel and Vue. You can find the source code on [GitHub](https://github.com/inertiajs/pingcrm). 6 | 7 | The Ping CRM demo is hosted on Heroku and the database is reset every hour. Please be respectful when editing data. 8 | 9 | 10 | [![Ping CRM](/images/pingcrm.png)](https://demo.inertiajs.com) 11 | 12 | 13 | In addition to the Vue version of Ping CRM, we also maintain a Svelte version of the application, which you can find [on GitHub](https://github.com/inertiajs/pingcrm-svelte). 14 | 15 | ## Third Party 16 | 17 | Beyond our official demo app, Ping CRM has also been translated into numerous different languages and frameworks. 18 | 19 | | Platform | Author | 20 | |:-------------------------------------|:---------------------| 21 | | [Clojure/React](https://github.com/prestancedesign/pingcrm-clojure) | Michaël Salihi | 22 | | [Echo/Vue](https://github.com/kohkimakimoto/pingcrm-echo) | Kohki Makimoto | 23 | | [Grails/Vue](https://github.com/matrei/pingcrm-grails) | Mattias Reichel | 24 | | [Laravel/React](https://github.com/Landish/pingcrm-react) | Lado Lomidze | 25 | | [Laravel/Mithril.js](https://github.com/tbreuss/pingcrm-mithril) | Thomas Breuss | 26 | | [Laravel/Svelte](https://github.com/zgabievi/pingcrm-svelte) | Zura Gabievi | 27 | | [Ruby on Rails/Vue](https://github.com/ledermann/pingcrm/) | Georg Ledermann | 28 | | [Symfony/Vue](https://github.com/aleksblendwerk/pingcrm-symfony) | Aleks Seltenreich | 29 | | [Yii 2/Vue](https://github.com/tbreuss/pingcrm-yii2) | Thomas Breuss | 30 | -------------------------------------------------------------------------------- /v2/installation/community-adapters.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Community Adapters 3 | --- 4 | 5 | In addition to the officially supported [Laravel](https://laravel.com/) adapter, there are also 6 | numerous community built server-side adapters available: 7 | 8 | - [AdonisJS](https://docs.adonisjs.com/guides/inertia) 9 | - [ASP.NET](https://github.com/kapi2289/InertiaCore) 10 | - [CakePHP InertiaJS Plugin](https://github.com/CakeDC/cakephp-inertia) 11 | - [CakePHP](https://github.com/ishanvyas22/cakephp-inertiajs) 12 | - [CanJS](https://github.com/cherifGsoul/inertia-can) 13 | - [Clojure](https://github.com/prestancedesign/inertia-clojure) 14 | - [ColdBox](https://github.com/elpete/cbInertia) 15 | - [Django](https://pypi.org/project/inertia-django/) 16 | - [Echo](https://github.com/kohkimakimoto/inertia-echo) 17 | - [Flask](https://github.com/j0ack/flask-inertia) 18 | - [Go (gonertia)](https://github.com/romsar/gonertia) 19 | - [Go (inertia-go)](https://github.com/petaki/inertia-go) 20 | - [Grails](https://github.com/matrei/grails-inertia-plugin) 21 | - [Kirby CMS](https://github.com/tobimori/kirby-inertia) 22 | - [Mako](https://github.com/inventor96/inertia-mako/) 23 | - [Masonite](https://github.com/girardinsamuel/masonite-inertia/) 24 | - [Mithril.js](https://github.com/maicol07/inertia-mithril) 25 | - [NestJS](https://github.com/harisfi/nestjs-inertia) 26 | - [Oak](https://github.com/jcs224/oak_inertia) 27 | - [Phoenix](https://github.com/inertiajs/inertia-phoenix) 28 | - [PSR-15](https://github.com/cherifGsoul/inertia-psr15) 29 | - [Rails](https://github.com/inertiajs/inertia-rails) 30 | - [Rust](https://github.com/stuarth/inertia-rs) 31 | - [Rust](https://kaiofelps.github.io/inertia-rust/) 32 | - [Sails](https://github.com/sailscastshq/boring-stack/tree/main/packages/inertia-sails) 33 | - [Spring/Ktor](https://github.com/Inertia4J/inertia4j) 34 | - [Symfony](https://github.com/skipthedragon/inertia-bundle) 35 | - [WordPress](https://github.com/evo-mark/inertia-wordpress) 36 | - [Yii2](https://github.com/tbreuss/yii2-inertia) 37 | -------------------------------------------------------------------------------- /v1/advanced/asset-versioning.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Asset Versioning 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | One common challenge when building single-page apps is refreshing site assets when they've been changed. Thankfully, Inertia makes this easy by optionally tracking the current version of your site assets. When an asset changes, Inertia will automatically make a full page visit instead of a XHR visit on the next request. 8 | 9 | ## Configuration 10 | 11 | To enable automatic asset refreshing, you need to tell Inertia the current version of your assets. This can be any arbitrary string (letters, numbers, or a file hash), as long as it changes when your assets have been updated. 12 | 13 | Typically, your application's asset version can be specified within the `version` method of the Inertia `HandleInertiaRequests` middleware. 14 | 15 | ```php 16 | class HandleInertiaRequests extends Middleware 17 | { 18 | public function version(Request $request) 19 | { 20 | return parent::version($request); 21 | } 22 | } 23 | ``` 24 | 25 | Alternatively, the asset version can be provided manually using the `Inertia::version()` method. 26 | 27 | ```php 28 | use Inertia\Inertia; 29 | 30 | Inertia::version($version); 31 | Inertia::version(fn () => $version); // Lazily... 32 | ``` 33 | 34 | ## Cache Busting 35 | 36 | Asset refreshing in Inertia works on the assumption that a hard page visit will trigger your assets to reload. However, Inertia doesn't actually do anything to force this. Typically this is done with some form of cache busting. For example, appending a version query parameter to the end of your asset URLs. 37 | 38 | If you're using Laravel Mix, you can do this automatically by enabling [versioning](https://laravel.com/docs/mix#versioning-and-cache-busting) in your `webpack.mix.js` file. When using Laravel's Vite integration, asset versioning is done automatically. 39 | -------------------------------------------------------------------------------- /v2/core-concepts/how-it-works.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: How It Works 3 | --- 4 | 5 | ## Use the Tools You Love 6 | 7 | With Inertia you build applications just like you've always done with your server-side web framework of choice. You use your framework's existing functionality for routing, controllers, middleware, authentication, authorization, data fetching, and more. 8 | 9 | However, Inertia replaces your application's view layer. Instead of using server-side rendering via PHP or Ruby templates, the views returned by your application are JavaScript page components. This allows you to build your entire frontend using React, Vue, or Svelte, while still enjoying the productivity of Laravel or your preferred server-side framework. 10 | 11 | ## Intercepting Requests 12 | 13 | As you might expect, simply creating your frontend in JavaScript doesn't give you a single-page application experience. If you were to click a link, your browser would make a full page visit, which would then cause your client-side framework to reboot on the subsequent page load. This is where Inertia changes everything. 14 | 15 | At its core, Inertia is essentially a client-side routing library. It allows you to make page visits without forcing a full page reload. This is done using the `` component, a light-weight wrapper around a normal anchor link. When you click an Inertia link, Inertia intercepts the click and makes the visit via XHR instead. You can even make these visits programmatically in JavaScript using `router.visit()`. 16 | 17 | When Inertia makes an XHR visit, the server detects that it's an Inertia visit and, instead of returning a full HTML response, it returns a JSON response with the JavaScript page component name and data (props). Inertia then dynamically swaps out the previous page component with the new page component and updates the browser's history state. 18 | 19 | **The end result is a silky smooth single-page experience.** 20 | 21 | To learn more about the nitty-gritty, technical details of how Inertia works under the hood, check out the [protocol page](/v2/core-concepts/the-protocol). 22 | -------------------------------------------------------------------------------- /v2/the-basics/redirects.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Redirects 3 | --- 4 | 5 | When making a non-GET Inertia request manually or via a `` element, you should ensure that you always respond with a proper Inertia redirect response. 6 | 7 | For example, if your controller is creating a new user, your "store" endpoint should return a redirect back to a standard `GET` endpoint, such as your user "index" page. Inertia will automatically follow this redirect and update the page accordingly. 8 | 9 | ```php 10 | class UsersController extends Controller 11 | { 12 | public function index() 13 | { 14 | return Inertia::render('Users/Index', [ 15 | 'users' => User::all(), 16 | ]); 17 | } 18 | 19 | public function store(Request $request) 20 | { 21 | User::create( 22 | $request->validate([ 23 | 'name' => ['required', 'max:50'], 24 | 'email' => ['required', 'max:50', 'email'], 25 | ]) 26 | ); 27 | 28 | return to_route('users.index'); 29 | } 30 | } 31 | ``` 32 | 33 | ## 303 Response Code 34 | 35 | When redirecting after a `PUT`, `PATCH`, or `DELETE` request, you must use a `303` response code, otherwise the subsequent request will not be treated as a `GET`request. A `303` redirect is very similar to a `302` redirect; however, the follow-up request is explicitly changed to a `GET` request. 36 | 37 | If you're using one of our official server-side adapters, all redirects will automatically be converted to `303` redirects. 38 | 39 | ## External Redirects 40 | 41 | Sometimes it's necessary to redirect to an external website, or even another non-Inertia endpoint in your app while handling an Inertia request. This can be accomplished using a server-side initiated `window.location` visit via the `Inertia::location()` method. 42 | 43 | ```php 44 | return Inertia::location($url); 45 | ``` 46 | 47 | The `Inertia::location()` method will generate a `409 Conflict` response and include the destination URL in the `X-Inertia-Location` header. When this response is received client-side, Inertia will automatically perform a `window.location = url` visit. 48 | -------------------------------------------------------------------------------- /v1/core-concepts/how-it-works.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: How It Works 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | With Inertia you build applications just like you've always done with your server-side web framework of choice. You use your framework's existing functionality for routing, controllers, middleware, authentication, authorization, data fetching, and more. 8 | 9 | However, Inertia replaces your application's view layer. Instead of using server-side rendering via PHP or Ruby templates, the views returned by your application are JavaScript page components. This allows you to build your entire frontend using React, Vue, or Svelte, while still enjoying the productivity of Laravel or your preferred server-side framework. 10 | 11 | As you might expect, simply creating your frontend in JavaScript doesn't give you a single-page application experience. If you were to click a link, your browser would make a full page visit, which would then cause your client-side framework to reboot on the subsequent page load. This is where Inertia changes everything. 12 | 13 | At its core, Inertia is essentially a client-side routing library. It allows you to make page visits without forcing a full page reload. This is done using the `` component, a light-weight wrapper around a normal anchor link. When you click an Inertia link, Inertia intercepts the click and makes the visit via XHR instead. You can even make these visits programmatically in JavaScript using `router.visit()`. 14 | 15 | When Inertia makes an XHR visit, the server detects that it's an Inertia visit and, instead of returning a full HTML response, it returns a JSON response with the JavaScript page component name and data (props). Inertia then dynamically swaps out the previous page component with the new page component and updates the browser's history state. 16 | 17 | **The end result is a silky smooth single-page experience.** 🎉 18 | 19 | To learn more about the nitty-gritty, technical details of how Inertia works under the hood, check out the [protocol page](/v1/core-concepts/the-protocol). 20 | -------------------------------------------------------------------------------- /v1/the-basics/redirects.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Redirects 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | When making a non-GET Inertia request manually or via a `` element, you should ensure that you always respond with a proper Inertia redirect response. 8 | 9 | For example, if your controller is creating a new user, your "store" endpoint should return a redirect back to a standard `GET` endpoint, such as your user "index" page. Inertia will automatically follow this redirect and update the page accordingly. 10 | 11 | ```php 12 | class UsersController extends Controller 13 | { 14 | public function index() 15 | { 16 | return Inertia::render('Users/Index', [ 17 | 'users' => User::all(), 18 | ]); 19 | } 20 | 21 | public function store(Request $request) 22 | { 23 | User::create( 24 | $request->validate([ 25 | 'name' => ['required', 'max:50'], 26 | 'email' => ['required', 'max:50', 'email'], 27 | ]) 28 | ); 29 | 30 | return to_route('users.index'); 31 | } 32 | } 33 | ``` 34 | 35 | ## 303 Response Code 36 | 37 | When redirecting after a `PUT`, `PATCH`, or `DELETE` request, you must use a `303` response code, otherwise the subsequent request will not be treated as a `GET`request. A `303` redirect is very similar to a `302` redirect; however, the follow-up request is explicitly changed to a `GET` request. 38 | 39 | If you're using one of our official server-side adapters, all redirects will automatically be converted to `303` redirects. 40 | 41 | ## External Redirects 42 | 43 | Sometimes it's necessary to redirect to an external website, or even another non-Inertia endpoint in your app while handling an Inertia request. This can be accomplished using a server-side initiated `window.location` visit via the `Inertia::location()` method. 44 | 45 | ```php 46 | return Inertia::location($url); 47 | ``` 48 | 49 | The `Inertia::location()` method will generate a `409 Conflict` response and include the destination URL in the `X-Inertia-Location` header. When this response is received client-side, Inertia will automatically perform a `window.location = url` visit. 50 | -------------------------------------------------------------------------------- /v1/the-basics/routing.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Routing 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | ## Defining Routes 8 | 9 | When using Inertia, all of your application's routes are defined server-side. This means that you don't need Vue Router or React Router. Instead, you can simply define Laravel routes and return [Inertia responses](/v1/the-basics/responses) from those routes. 10 | 11 | ## Shorthand Routes 12 | 13 | If you have a [page](/v1/the-basics/pages) that doesn't need a corresponding controller method, like an "FAQ" or "about" page, you can route directly to a component via the `Route::inertia()` method. 14 | 15 | ```php 16 | Route::inertia('/about', 'About'); 17 | ``` 18 | 19 | ## Generating URLs 20 | 21 | Some server-side frameworks allow you to generate URLs from named routes. However, you will not have access to those helpers client-side. Here are a couple ways to still use named routes with Inertia. 22 | 23 | The first option is to generate URLs server-side and include them as props. Notice in this example how we're passing the `edit_url` and `create_url` to the `Users/Index` component. 24 | 25 | ```php 26 | class UsersController extends Controller 27 | { 28 | public function index() 29 | { 30 | return Inertia::render('Users/Index', [ 31 | 'users' => User::all()->map(function ($user) { 32 | return [ 33 | 'id' => $user->id, 34 | 'name' => $user->name, 35 | 'email' => $user->email, 36 | 'edit_url' => route('users.edit', $user), 37 | ]; 38 | }), 39 | 'create_url' => route('users.create'), 40 | ]); 41 | } 42 | } 43 | ``` 44 | 45 | However, when using Laravel, the [Ziggy](https://github.com/tightenco/ziggy) library can make your named, server-side routes available to you via a global `route()` function. In fact, if you are developing an application using one of Laravel's [starter kits](https://laravel.com/docs/starter-kits), Ziggy is already configured for you. 46 | 47 | If you're using Ziggy with Vue, it's helpful to make this function available as a custom `$route`property so you can use it directly in your templates. 48 | 49 | 50 | 51 | ```js Vue 2 icon="vuejs" 52 | Vue.prototype.$route = route 53 | ``` 54 | 55 | ```js Vue 3 icon="vuejs" 56 | app.config.globalProperties.$route = route 57 | ``` 58 | 59 | 60 | 61 | ```html 62 | Create User 63 | ``` 64 | -------------------------------------------------------------------------------- /v2/security/history-encryption.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: History Encryption 3 | --- 4 | 5 | Imagine a scenario where your user is authenticated, browses privileged information on your site, then logs out. If they press the back button, they can still see the privileged information that is stored in the window's history state. This is a security risk. To prevent this, Inertia.js provides a history encryption feature. 6 | 7 | ## How It Works 8 | 9 | When you instruct Inertia to encrypt your app's history, it uses the browser's built-in [`crypto` api ](https://developer.mozilla.org/en-US/docs/Web/API/Crypto) to encrypt the current page's data before pushing it to the history state. We store the corresponding key in the browser's session storage. When the user navigates back to a page, we decrypt the data using the key stored in the session storage. 10 | 11 | Once you instruct Inertia to clear your history state, we simply clear the existing key from session storage roll a new one. If we attempt to decrypt the history state with the new key, it will fail and Inertia will make a fresh request back to your server for the page data. 12 | 13 | History encryption relies on `window.crypto.subtle` which is only available in secure environments (sites with SSL enabled). 14 | 15 | ## Opting in 16 | 17 | History encryption is an opt-in feature. There are several methods for enabling it: 18 | 19 | ### Global Encryption 20 | 21 | If you'd like to enable history encryption globally, set the `inertia.history.encrypt` config value to `true`. 22 | 23 | You are able to opt out of encryption on specific pages by calling the `Inertia::encryptHistory()`method before returning the response. 24 | 25 | ```php 26 | Inertia::encryptHistory(false); 27 | ``` 28 | 29 | ### Per-request Encryption 30 | 31 | To encrypt the history of an individual request, simply call the `Inertia::encryptHistory()` method before returning the response. 32 | 33 | ```php 34 | Inertia::encryptHistory(); 35 | ``` 36 | 37 | ### Encrypt Middleware 38 | 39 | To encrypt a group of routes, you may use Inertia's included `EncryptHistory` middleware. 40 | 41 | ```php 42 | Route::middleware([Inertia\Middleware\EncryptHistory::class])->get('/', function() { 43 | // 44 | }); 45 | 46 | Route::middleware(['inertia::encrypt'])->get('/', function() { 47 | // 48 | }); 49 | ``` 50 | 51 | ## Clearing History 52 | 53 | To clear the history state, you can call the `Inertia::clearHistory()` method before returning the response. 54 | 55 | ```php 56 | Inertia::clearHistory(); 57 | ``` 58 | 59 | Once the response has rendered on the client, the encryption key will be rotated, rendering the previous history state unreadable. 60 | 61 | You can also clear history on the client site by calling `router.clearHistory()`. 62 | -------------------------------------------------------------------------------- /v2/advanced/asset-versioning.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Asset Versioning 3 | --- 4 | 5 | One common challenge when building single-page apps is refreshing site assets when they've been changed. Thankfully, Inertia makes this easy by optionally tracking the current version of your site assets. When an asset changes, Inertia will automatically make a full page visit instead of a XHR visit on the next request. 6 | 7 | ## Configuration 8 | 9 | To enable automatic asset refreshing, you need to tell Inertia the current version of your assets. This can be any arbitrary string (letters, numbers, or a file hash), as long as it changes when your assets have been updated. 10 | 11 | Typically, your application's asset version can be specified within the `version` method of the Inertia `HandleInertiaRequests` middleware. 12 | 13 | ```php 14 | class HandleInertiaRequests extends Middleware 15 | { 16 | public function version(Request $request) 17 | { 18 | return parent::version($request); 19 | } 20 | } 21 | ``` 22 | 23 | Alternatively, the asset version can be provided manually using the `Inertia::version()` method. 24 | 25 | ```php 26 | use Inertia\Inertia; 27 | 28 | Inertia::version($version); 29 | Inertia::version(fn () => $version); // Lazily... 30 | ``` 31 | 32 | ## Cache Busting 33 | 34 | Asset refreshing in Inertia works on the assumption that a hard page visit will trigger your assets to reload. However, Inertia doesn't actually do anything to force this. Typically this is done with some form of cache busting. For example, appending a version query parameter to the end of your asset URLs. 35 | 36 | With Laravel's Vite integration, asset versioning is done automatically. If you're using Laravel Mix, you can do this automatically by enabling [versioning](https://laravel.com/docs/mix#versioning-and-cache-busting) in your `webpack.mix.js` file. 37 | 38 | ## Manual Refreshing 39 | 40 | If you want to take asset refreshing into your control, you can return a fixed value from the `version` method in the `HandleInertiaRequests` middleware. This disables Inertia's automatic asset versioning. 41 | 42 | For example, if you want to notify users when a new version of your frontend is available, you can still expose the actual asset version to the frontend by including it as [shared data](/v2/data-props/shared-data). 43 | 44 | ```php 45 | class HandleInertiaRequests extends Middleware 46 | { 47 | public function version(Request $request) 48 | { 49 | return null; 50 | } 51 | 52 | public function share(Request $request) 53 | { 54 | return array_merge(parent::share($request), [ 55 | 'version' => parent::version($request), 56 | ]); 57 | } 58 | } 59 | ``` 60 | 61 | On the frontend, you can watch the `version` property and show a notification when a new version is detected. 62 | -------------------------------------------------------------------------------- /v2/advanced/code-splitting.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Code Splitting 3 | --- 4 | 5 | Code splitting breaks apart the various pages of your application into smaller bundles, which are then loaded on demand when visiting new pages. This can significantly reduce the size of the initial JavaScript bundle loaded by the browser, improving the time to first render. 6 | 7 | While code splitting is helpful for very large projects, it does require extra requests when visiting new pages. Generally speaking, if you're able to use a single bundle, your app is going to feel snappier. 8 | 9 | To enable code splitting, you will need to tweak the `resolve` callback in your `createInertiaApp()` configuration, and how you do this is different depending on which bundler you're using. 10 | 11 | ## Using Vite 12 | 13 | Vite enables code splitting (or lazy-loading as they call it) by default when using their `import.meta.glob()` function, so simply omit the `{ eager: true }` option, or set it to `false`, to disable eager loading. 14 | 15 | 16 | 17 | ```js Vue icon="vuejs" 18 | resolve: name => { 19 | const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) // [!code --:2] 20 | return pages[`./Pages/${name}.vue`] 21 | const pages = import.meta.glob('./Pages/**/*.vue') // [!code ++:2] 22 | return pages[`./Pages/${name}.vue`]() 23 | }, 24 | ``` 25 | 26 | ```js React icon="react" 27 | resolve: name => { 28 | const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true }) // [!code --:2] 29 | return pages[`./Pages/${name}.jsx`] 30 | const pages = import.meta.glob('./Pages/**/*.jsx') // [!code ++:2] 31 | return pages[`./Pages/${name}.jsx`]() 32 | }, 33 | ``` 34 | 35 | ```js Svelte icon="s" 36 | resolve: name => { 37 | const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true }) // [!code --:2] 38 | return pages[`./Pages/${name}.svelte`] 39 | const pages = import.meta.glob('./Pages/**/*.svelte') // [!code ++:2] 40 | return pages[`./Pages/${name}.svelte`]() 41 | }, 42 | ``` 43 | 44 | 45 | 46 | ## Using Webpack 47 | 48 | To use code splitting with Webpack, you will first need to enable [dynamic imports](https://github.com/tc39/proposal-dynamic-import) via a Babel plugin. Let's install it now. 49 | 50 | ```bash 51 | npm install @babel/plugin-syntax-dynamic-import 52 | ``` 53 | 54 | Next, create a `.babelrc` file in your project with the following configuration: 55 | 56 | ```json 57 | { 58 | "plugins": ["@babel/plugin-syntax-dynamic-import"] 59 | } 60 | ``` 61 | 62 | If you're using Laravel Mix, the dynamic imports Babel plugin is already installed and configured, and you can skip these steps. We recommend using Laravel Mix 6 or above, as there are known issues with older versions. 63 | 64 | Finally, update the `resolve` callback in your app's initialization code to use `import`instead of `require`. 65 | 66 | 67 | 68 | ```js Vue icon="vuejs" 69 | resolve: name => require(`./Pages/${name}`), // [!code --] 70 | resolve: name => import(`./Pages/${name}`), // [!code ++] 71 | ``` 72 | 73 | ```js React icon="react" 74 | resolve: name => require(`./Pages/${name}`), // [!code --] 75 | resolve: name => import(`./Pages/${name}`), // [!code ++] 76 | ``` 77 | 78 | ```js Svelte icon="s" 79 | resolve: name => require(`./Pages/${name}.svelte`), // [!code --] 80 | resolve: name => import(`./Pages/${name}.svelte`), // [!code ++] 81 | ``` 82 | 83 | 84 | 85 | You should also consider using cache busting to force browsers to load the latest version of your assets. To accomplish this, add the following configuration to your webpack configuration file. 86 | 87 | ```js 88 | output: { 89 | chunkFilename: 'js/[name].js?id=[chunkhash]', 90 | } 91 | ``` 92 | -------------------------------------------------------------------------------- /v1/installation/server-side-setup.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Server-side Setup 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | The first step when installing Inertia is to configure your server-side framework. Inertia maintains an official server-side adapter for [Laravel](https://laravel.com/). For other frameworks, please see the [community adapters](/community-adapters). 8 | 9 | Inertia is fine-tuned for Laravel, so the documentation examples on this website utilize Laravel. For examples of using Inertia with other server-side frameworks, please refer to the framework specific documentation maintained by that adapter. 10 | 11 | ## Laravel Starter Kits 12 | 13 | Laravel's [starter kits](https://laravel.com/docs/starter-kits), Breeze and Jetstream, provide out-of-the-box scaffolding for new Inertia applications. These starter kits are the absolute fastest way to start building a new Inertia project using Laravel and Vue or React. However, if you would like to manually install Inertia into your application, please consult the documentation below. 14 | 15 | ## Install Dependencies 16 | 17 | First, install the Inertia server-side adapter using the Composer package manager. 18 | 19 | ```bash 20 | composer require inertiajs/inertia-laravel 21 | ``` 22 | 23 | ## Root Template 24 | 25 | Next, setup the root template that will be loaded on the first page visit to your application. This will be used to load your site assets (CSS and JavaScript), and will also contain a root `
` in which to boot your JavaScript application. 26 | 27 | ```blade 28 | 29 | 30 | 31 | 32 | @vite('resources/js/app.js') 33 | @inertiaHead 34 | 35 | 36 | @inertia 37 | 38 | 39 | ``` 40 | 41 | This template should include your assets, as well as the `@inertia` and `@inertiaHead`directives. 42 | 43 | By default, Inertia's Laravel adapter will assume your root template is named `app.blade.php`. If you would like to use a different root view, you can change it using the `Inertia::setRootView()` method. 44 | 45 | ## Middleware 46 | 47 | Next we need to setup the Inertia middleware. You can accomplish this by publishing the `HandleInertiaRequests` middleware to your application, which can be done using the following Artisan command. 48 | 49 | ```sh 50 | php artisan inertia:middleware 51 | ``` 52 | 53 | Once the middleware has been published, append the `HandleInertiaRequests` middleware to the `web` middleware group in your application's `bootstrap/app.php` file. 54 | 55 | ```php 56 | use App\Http\Middleware\HandleInertiaRequests; 57 | 58 | ->withMiddleware(function (Middleware $middleware) { 59 | $middleware->web(append: [ 60 | HandleInertiaRequests::class, 61 | ]); 62 | }) 63 | ``` 64 | 65 | This middleware provides a `version()` method for setting your [asset version](/v1/advanced/asset-versioning), as well as a `share()` method for defining [shared data](/v1/the-basics/shared-data). 66 | 67 | ## Creating Responses 68 | 69 | That's it, you're all ready to go server-side! Now you're ready to start creating Inertia [pages](/v1/the-basics/pages) and rendering them via [responses](/v1/the-basics/responses). 70 | 71 | ```php 72 | use Inertia\Inertia; 73 | 74 | class EventsController extends Controller 75 | { 76 | public function show(Event $event) 77 | { 78 | return Inertia::render('Event/Show', [ 79 | 'event' => $event->only( 80 | 'id', 81 | 'title', 82 | 'start_date', 83 | 'description' 84 | ), 85 | ]); 86 | } 87 | } 88 | ``` 89 | -------------------------------------------------------------------------------- /v2/getting-started/upgrade-guide.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Upgrade Guide for v2.0 3 | --- 4 | 5 | You can find the legacy docs for Inertia.js v1.0 at [inertiajs.com/docs/v1](/v1). 6 | 7 | ## What's New 8 | 9 | Inertia.js v2.0 is a huge step forward for Inertia! The core library has been completely rewritten to architecturally support asynchronous requests, enabling a whole set of new features, including: 10 | 11 | 12 | 13 | Keep data fresh by automatically polling the server at a specified interval. 14 | 15 | 16 | Speed up navigation by prefetching data for links when they become visible. 17 | 18 | 19 | Load non-essential data after the initial page load to improve performance. 20 | 21 | 22 | Seamlessly load more data as the user scrolls down the page. 23 | 24 | 25 | Load data only when it becomes visible in the viewport. 26 | 27 | 28 | Clear page data from browser history state when logging out of an application. 29 | 30 | 31 | 32 | 33 | ## Upgrade Dependencies 34 | 35 | To upgrade to the Inertia.js v2.0, first use npm to install the client-side adapter of your choice: 36 | 37 | 38 | 39 | ```bash Vue icon="vuejs" 40 | npm install @inertiajs/vue3@^2.0 41 | ``` 42 | 43 | ```bash React icon="react" 44 | npm install @inertiajs/react@^2.0 45 | ``` 46 | 47 | ```bash Svelte icon="s" 48 | npm install @inertiajs/svelte@^2.0 49 | ``` 50 | 51 | 52 | 53 | Next, upgrade the `inertiajs/inertia-laravel` package to use the `2.x` dev branch: 54 | 55 | ```bash 56 | composer require inertiajs/inertia-laravel:^2.0 57 | ``` 58 | 59 | ## Breaking Changes 60 | 61 | While a significant release, Inertia.js v2.0 doesn't introduce many breaking changes. Here's a list of all the breaking changes: 62 | 63 | ### Dropped Laravel 8 and 9 Support 64 | 65 | The Laravel adapter now requires Laravel 10 and PHP 8.1 at a minimum. 66 | 67 | ### Dropped Vue 2 Support 68 | 69 | The Vue 2 adapter has been removed. Vue 2 reached End of Life on December 3, 2023, so this felt like it was time. 70 | 71 | ### Router `replace` Method 72 | 73 | The previously deprecated `router.replace` method has been re-instated, but its functionality has changed. It is now used to make [Client Side](/v2/the-basics/manual-visits#client-side-visits) page visits. To make server-side visits that replace the current history entry in the browser, use the `replace` option: 74 | 75 | ```javascript 76 | router.get('/users', { search: 'John' }, { replace: true }) 77 | ``` 78 | 79 | ### Svelte Adapter 80 | 81 | - Dropped support for Svelte 3 as it reached End of Life on June 20, 2023. 82 | - The `remember` helper has been rename to `useRemember` to be consistent with other helpers. 83 | - Updated `setup` callback in `app.js`. You need to pass `props` when initializing the `App` component. [See setup in app.js](/v2/installation/client-side-setup#initialize-the-inertia-app) 84 | - `setup` callback is now required in `ssr.js`. [See setup in ssr.js](/v2/advanced/server-side-rendering#add-server-entry-point) 85 | 86 | ### Partial Reloads Are Now Async 87 | 88 | Previously partial reloads in Inertia were synchronous, just like all Inertia requests. In v2.0, partial reloads are now asynchronous. Generally this is desirable, but if you were relying on these requests being synchronous, you may need to adjust your code. 89 | -------------------------------------------------------------------------------- /v2/the-basics/routing.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Routing 3 | --- 4 | 5 | ## Defining Routes 6 | 7 | When using Inertia, all of your application's routes are defined server-side. This means that you don't need Vue Router or React Router. Instead, you can simply define Laravel routes and return [Inertia responses](/v2/the-basics/responses) from those routes. 8 | 9 | ## Shorthand Routes 10 | 11 | If you have a [page](/v2/the-basics/pages) that doesn't need a corresponding controller method, like an "FAQ" or "about" page, you can route directly to a component via the `Route::inertia()` method. 12 | 13 | ```php 14 | Route::inertia('/about', 'About'); 15 | ``` 16 | 17 | ## Generating URLs 18 | 19 | Some server-side frameworks allow you to generate URLs from named routes. However, you will not have access to those helpers client-side. Here are a couple ways to still use named routes with Inertia. 20 | 21 | The first option is to generate URLs server-side and include them as props. Notice in this example how we're passing the `edit_url` and `create_url` to the `Users/Index` component. 22 | 23 | ```php 24 | class UsersController extends Controller 25 | { 26 | public function index() 27 | { 28 | return Inertia::render('Users/Index', [ 29 | 'users' => User::all()->map(fn ($user) => [ 30 | 'id' => $user->id, 31 | 'name' => $user->name, 32 | 'email' => $user->email, 33 | 'edit_url' => route('users.edit', $user), 34 | ]), 35 | 'create_url' => route('users.create'), 36 | ]); 37 | } 38 | } 39 | ``` 40 | 41 | When using Laravel, you have several options to make your server-side routes available to your client-side code: 42 | 43 | ### Wayfinder 44 | 45 | When using [Wayfinder](https://github.com/laravel/wayfinder), you can pass the generated TypeScript method directly to the [Link component](/v2/the-basics/links#wayfinder), [form helpers](/v2/the-basics/forms#wayfinder), or [router methods](/v2/the-basics/manual-visits#wayfinder) and Inertia understand how to handle it. In fact, if you are developing an application using one of Laravel's [starter kits](https://laravel.com/docs/starter-kits), Wayfinder is already configured for you. 46 | 47 | ### Ziggy 48 | 49 | The [Ziggy](https://github.com/tighten/ziggy) library can make your named, server-side routes available to you via a global `route()` function. If you're using the Vue plugin included with Ziggy, you may use the `route()` function directly in your templates. 50 | 51 | ```vue 52 | Create User 53 | ``` 54 | 55 | When [server-side rendering](/v2/advanced/server-side-rendering) is enabled, you may pass an options object to the Ziggy plugin in your `ssr.js` file. This should include the route definitions and current location. 56 | 57 | ```js 58 | .use(ZiggyVue, { 59 | ...page.props.ziggy, 60 | location: new URL(page.props.ziggy.location), 61 | }); 62 | ``` 63 | 64 | ## Customizing the Page URL 65 | 66 | The [page object](/v2/core-concepts/the-protocol#the-page-object) includes a `url` that represents the current page's URL. By default, the Laravel adapter resolves this using the `fullUrl()` method on the `Request` instance, but strips the scheme and host so the result is a relative URL. 67 | 68 | If you need to customize how the URL is resolved, you may provide a resolver within the `urlResolver`method of the Inertia `HandleInertiaRequests` middleware. 69 | 70 | ```php 71 | class HandleInertiaRequests extends Middleware 72 | { 73 | public function urlResolver() 74 | { 75 | return function (Request $request) { 76 | // Return the URL for the request... 77 | }; 78 | } 79 | } 80 | ``` 81 | 82 | Alternatively, you may define the resolver using the `Inertia::resolveUrlUsing()` method. 83 | 84 | ```php 85 | Inertia::resolveUrlUsing(function (Request $request) { 86 | // Return the URL for the request... 87 | }); 88 | ``` 89 | -------------------------------------------------------------------------------- /v2/data-props/polling.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Polling 3 | --- 4 | 5 | ## Poll Helper 6 | 7 | Polling your server for new information on the current page is common, so Inertia provides a poll helper designed to help reduce the amount of boilerplate code. In addition, the poll helper will automatically stop polling when the page is unmounted. 8 | 9 | The only required argument is the polling interval in milliseconds. 10 | 11 | 12 | 13 | ```js Vue icon="vuejs" 14 | import { usePoll } from '@inertiajs/vue3' 15 | 16 | usePoll(2000) 17 | ``` 18 | 19 | ```jsx React icon="react" 20 | import { usePoll } from '@inertiajs/react' 21 | 22 | usePoll(2000) 23 | ``` 24 | 25 | ```js Svelte icon="s" 26 | import { usePoll } from '@inertiajs/svelte' 27 | 28 | usePoll(2000) 29 | ``` 30 | 31 | 32 | 33 | If you need to pass additional request options to the poll helper, you can pass any of the `router.reload` options as the second parameter. 34 | 35 | 36 | 37 | ```js Vue icon="vuejs" 38 | import { usePoll } from '@inertiajs/vue3' 39 | 40 | usePoll(2000, { 41 | onStart() { 42 | console.log('Polling request started') 43 | }, 44 | onFinish() { 45 | console.log('Polling request finished') 46 | } 47 | }) 48 | ``` 49 | 50 | ```jsx React icon="react" 51 | import { usePoll } from '@inertiajs/react' 52 | 53 | usePoll(2000, { 54 | onStart() { 55 | console.log('Polling request started') 56 | }, 57 | onFinish() { 58 | console.log('Polling request finished') 59 | } 60 | }) 61 | ``` 62 | 63 | ```js Svelte icon="s" 64 | import { usePoll } from '@inertiajs/svelte' 65 | 66 | usePoll(2000, { 67 | onStart() { 68 | console.log('Polling request started') 69 | }, 70 | onFinish() { 71 | console.log('Polling request finished') 72 | } 73 | }) 74 | ``` 75 | 76 | 77 | 78 | If you'd like more control over the polling behavior, the poll helper provides `stop` and `start` methods that allow you to manually start and stop polling. You can pass the `autoStart: false` option to the poll helper to prevent it from automatically starting polling when the component is mounted. 79 | 80 | 81 | 82 | ```vue Vue icon="vuejs" 83 | 90 | 91 | 95 | ``` 96 | 97 | ```jsx React icon="react" 98 | import { usePoll } from '@inertiajs/react' 99 | 100 | export default () => { 101 | const { start, stop } = usePoll(2000, {}, { 102 | autoStart: false, 103 | }) 104 | 105 | return ( 106 |
107 | 108 | 109 |
110 | ) 111 | } 112 | ``` 113 | 114 | ```js Svelte icon="s" 115 | import { usePoll } from '@inertiajs/svelte' 116 | 117 | const { start, stop } = usePoll(2000, {}, { 118 | autoStart: false, 119 | }) 120 | ``` 121 | 122 |
123 | 124 | ## Throttling 125 | 126 | By default, the poll helper will throttle requests by 90% when the browser tab is in the background. If you'd like to disable this behavior, you can pass the `keepAlive` option to the poll helper. 127 | 128 | 129 | 130 | ```js Vue icon="vuejs" 131 | import { usePoll } from '@inertiajs/vue3' 132 | 133 | usePoll(2000, {}, { 134 | keepAlive: true, 135 | }) 136 | ``` 137 | 138 | ```jsx React icon="react" 139 | import { usePoll } from '@inertiajs/react' 140 | 141 | usePoll(2000, {}, { 142 | keepAlive: true, 143 | }) 144 | ``` 145 | 146 | ```js Svelte icon="s" 147 | import { usePoll } from '@inertiajs/svelte' 148 | 149 | usePoll(2000, {}, { 150 | keepAlive: true, 151 | }) 152 | ``` 153 | 154 | 155 | -------------------------------------------------------------------------------- /v2/advanced/scroll-management.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scroll Management 3 | --- 4 | 5 | ## Scroll Resetting 6 | 7 | When navigating between pages, Inertia mimics default browser behavior by automatically resetting the scroll position of the document body (as well as any [scroll regions](#scroll-regions) you've defined) back to the top. 8 | 9 | In addition, Inertia keeps track of the scroll position of each page and automatically restores that scroll position as you navigate forward and back in history. 10 | 11 | ## Scroll Preservation 12 | 13 | Sometimes it's desirable to prevent the default scroll resetting when making visits. You can disable this behavior by setting the `preserveScroll` option to `true`. 14 | 15 | 16 | 17 | ```js Vue icon="vuejs" 18 | import { router } from '@inertiajs/vue3' 19 | 20 | router.visit(url, { preserveScroll: true }) 21 | ``` 22 | 23 | ```js React icon="react" 24 | import { router } from '@inertiajs/react' 25 | 26 | router.visit(url, { preserveScroll: true }) 27 | ``` 28 | 29 | ```js Svelte icon="s" 30 | import { router } from '@inertiajs/svelte' 31 | 32 | router.visit(url, { preserveScroll: true }) 33 | ``` 34 | 35 | 36 | 37 | If you'd like to only preserve the scroll position if the response includes validation errors, set the `preserveScroll` option to "errors". 38 | 39 | 40 | 41 | ```js Vue icon="vuejs" 42 | import { router } from '@inertiajs/vue3' 43 | 44 | router.visit(url, { preserveScroll: 'errors' }) 45 | ``` 46 | 47 | ```js React icon="react" 48 | import { router } from '@inertiajs/react' 49 | 50 | router.visit(url, { preserveScroll: 'errors' }) 51 | ``` 52 | 53 | ```js Svelte icon="s" 54 | import { router } from '@inertiajs/svelte' 55 | 56 | router.visit(url, { preserveScroll: 'errors' }) 57 | ``` 58 | 59 | 60 | 61 | You can also lazily evaluate the `preserveScroll` option based on the response by providing a callback. 62 | 63 | 64 | 65 | ```js Vue icon="vuejs" 66 | import { router } from '@inertiajs/vue3' 67 | 68 | router.post('/users', data, { 69 | preserveScroll: (page) => page.props.someProp === 'value', 70 | }) 71 | ``` 72 | 73 | ```js React icon="react" 74 | import { router } from '@inertiajs/react' 75 | 76 | router.post('/users', data, { 77 | preserveScroll: (page) => page.props.someProp === 'value', 78 | }) 79 | ``` 80 | 81 | ```js Svelte icon="s" 82 | import { router } from '@inertiajs/svelte' 83 | 84 | router.post('/users', data, { 85 | preserveScroll: (page) => page.props.someProp === 'value', 86 | }) 87 | ``` 88 | 89 | 90 | 91 | When using an [Inertia link](/v2/the-basics/links), you can preserve the scroll position using the `preserveScroll` prop. 92 | 93 | 94 | 95 | ```vue Vue icon="vuejs" 96 | import { Link } from '@inertiajs/vue3' 97 | 98 | Home 99 | ``` 100 | 101 | ```jsx React icon="react" 102 | import { Link } from '@inertiajs/react' 103 | 104 | Home 105 | ``` 106 | 107 | ```svelte Svelte icon="s" 108 | import { inertia, Link } from '@inertiajs/svelte' 109 | 110 | Home 111 | 112 | Home 113 | ``` 114 | 115 | 116 | 117 | ## Scroll Regions 118 | 119 | If your app doesn't use document body scrolling, but instead has scrollable elements (using the `overflow` CSS property), scroll resetting will not work. 120 | 121 | In these situations, you must tell Inertia which scrollable elements to manage by adding the `scroll-region` attribute to the element. 122 | 123 | ```html 124 |
125 | 126 |
127 | ``` 128 | 129 | ## Text Fragments 130 | 131 | [Text fragments](https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Fragment/Text_fragments) allow you to link directly to specific text on a page using a special URL syntax like `#:~:text=term`. However, the browser removes the fragment directive before any JavaScript runs, so text fragments only work if the targeted text is present in the initial HTML response. 132 | 133 | To use text fragments with your Inertia pages, enable [server-side rendering](/v2/advanced/server-side-rendering). 134 | -------------------------------------------------------------------------------- /v2/security/csrf-protection.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CSRF Protection 3 | --- 4 | 5 | ## Making Requests 6 | 7 | Laravel automatically includes the proper CSRF token when making requests via Inertia or Axios. However, if you're using Laravel, be sure to omit the `csrf-token` meta tag from your project, as this will prevent the CSRF token from refreshing properly. 8 | 9 | If your server-side framework includes cross-site request forgery (CSRF) protection, you'll need to ensure that each Inertia request includes the necessary CSRF token for `POST`, `PUT`, `PATCH`, and `DELETE` requests. 10 | 11 | Of course, as already discussed, some server-side frameworks such as Laravel automatically handle the inclusion of the CSRF token when making requests. **Therefore, no additional configuration is required when using one of these frameworks.** 12 | 13 | However, if you need to handle CSRF protection manually, one approach is to include the CSRF token as a prop on every response. You can then use the token when making Inertia requests. 14 | 15 | 16 | 17 | ```js Vue icon="vuejs" 18 | import { router, usePage } from '@inertiajs/vue3' 19 | 20 | const page = usePage() 21 | 22 | router.post('/users', { 23 | _token: page.props.csrf_token, 24 | name: 'John Doe', 25 | email: 'john.doe@example.com', 26 | }) 27 | ``` 28 | 29 | ```js React icon="react" 30 | import { router, usePage } from '@inertiajs/react' 31 | 32 | const props = usePage().props 33 | 34 | router.post('/users', { 35 | _token: props.csrf_token, 36 | name: 'John Doe', 37 | email: 'john.doe@example.com', 38 | }) 39 | ``` 40 | 41 | ```js Svelte icon="s" 42 | import { page, router } from '@inertiajs/svelte' 43 | 44 | router.post('/users', { 45 | _token: $page.props.csrf_token, 46 | name: 'John Doe', 47 | email: 'john.doe@example.com', 48 | }) 49 | ``` 50 | 51 | 52 | 53 | You can even use Inertia's [shared data](/v2/data-props/shared-data) functionality to automatically include the `csrf_token` with each response. 54 | 55 | However, a better approach is to use the CSRF functionality already built into [axios](https://github.com/axios/axios) for this. Axios is the HTTP library that Inertia uses under the hood. 56 | 57 | Axios automatically checks for the existence of an `XSRF-TOKEN` cookie. If it's present, it will then include the token in an `X-XSRF-TOKEN` header for any requests it makes. 58 | 59 | The easiest way to implement this is using server-side middleware. Simply include the `XSRF-TOKEN`cookie on each response, and then verify the token using the `X-XSRF-TOKEN` header sent in the requests from axios. 60 | 61 | ## Handling Mismatches 62 | 63 | When a CSRF token mismatch occurs, your server-side framework will likely throw an exception that results in an error response. For example, when using Laravel, a `TokenMismatchException` is thrown which results in a `419` error page. Since that isn't a valid Inertia response, the error is shown in a modal. 64 | 65 | 66 | 67 | Obviously, this isn't a great user experience. A better way to handle these errors is to return a redirect back to the previous page, along with a flash message that the page expired. This will result in a valid Inertia response with the flash message available as a prop which you can then display to the user. Of course, you'll need to share your [flash messages](/shared-data#flash-messages) with Inertia for this to work. 68 | 69 | When using Laravel, you may modify your application's exception handler to automatically redirect the user back to the page they were previously on while flashing a message to the session. To accomplish this, you may use the `respond` exception method in your application's `bootstrap/app.php` file. 70 | 71 | ```php 72 | use Symfony\Component\HttpFoundation\Response; 73 | 74 | ->withExceptions(function (Exceptions $exceptions) { 75 | $exceptions->respond(function (Response $response) { 76 | if ($response->getStatusCode() === 419) { 77 | return back()->with([ 78 | 'message' => 'The page expired, please try again.', 79 | ]); 80 | } 81 | 82 | return $response; 83 | }); 84 | }); 85 | ``` 86 | 87 | The end result is a much better experience for your users. Instead of seeing the error modal, the user is instead presented with a message that the page "expired" and are asked to try again. 88 | 89 | 90 | -------------------------------------------------------------------------------- /v1/advanced/scroll-management.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Scroll Management 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | ## Scroll Resetting 8 | 9 | When navigating between pages, Inertia mimics default browser behavior by automatically resetting the scroll position of the document body (as well as any [scroll regions](#scroll-regions) you've defined) back to the top. 10 | 11 | In addition, Inertia keeps track of the scroll position of each page and automatically restores that scroll position as you navigate forward and back in history. 12 | 13 | ## Scroll Preservation 14 | 15 | Sometimes it's desirable to prevent the default scroll resetting when making visits. You can disable this behavior by setting the `preserveScroll` option to `false`. 16 | 17 | 18 | 19 | ```js Vue 2 icon="vuejs" 20 | import { router } from '@inertiajs/vue2' 21 | 22 | router.visit(url, { preserveScroll: false }) 23 | ``` 24 | 25 | ```js Vue 3 icon="vuejs" 26 | import { router } from '@inertiajs/vue3' 27 | 28 | router.visit(url, { preserveScroll: false }) 29 | ``` 30 | 31 | ```js React icon="react" 32 | import { router } from '@inertiajs/react' 33 | 34 | router.visit(url, { preserveScroll: false }) 35 | ``` 36 | 37 | ```js Svelte icon="s" 38 | import { router } from '@inertiajs/svelte' 39 | 40 | router.visit(url, { preserveScroll: false }) 41 | ``` 42 | 43 | 44 | 45 | If you'd like to only preserve the scroll position if the response includes validation errors, set the `preserveScroll` option to "errors". 46 | 47 | 48 | 49 | ```js Vue 2 icon="vuejs" 50 | import { router } from '@inertiajs/vue2' 51 | 52 | router.visit(url, { preserveScroll: 'errors' }) 53 | ``` 54 | 55 | ```js Vue 3 icon="vuejs" 56 | import { router } from '@inertiajs/vue3' 57 | 58 | router.visit(url, { preserveScroll: 'errors' }) 59 | ``` 60 | 61 | ```js React icon="react" 62 | import { router } from '@inertiajs/react' 63 | 64 | router.visit(url, { preserveScroll: 'errors' }) 65 | ``` 66 | 67 | ```js Svelte icon="s" 68 | import { router } from '@inertiajs/svelte' 69 | 70 | router.visit(url, { preserveScroll: 'errors' }) 71 | ``` 72 | 73 | 74 | 75 | You can also lazily evaluate the `preserveScroll` option based on the response by providing a callback. 76 | 77 | 78 | 79 | ```js Vue 2 icon="vuejs" 80 | import { router } from '@inertiajs/vue2' 81 | 82 | router.post('/users', data, { 83 | preserveScroll: (page) => page.props.someProp === 'value', 84 | }) 85 | ``` 86 | 87 | ```js Vue 3 icon="vuejs" 88 | import { router } from '@inertiajs/vue3' 89 | 90 | router.post('/users', data, { 91 | preserveScroll: (page) => page.props.someProp === 'value', 92 | }) 93 | ``` 94 | 95 | ```js React icon="react" 96 | import { router } from '@inertiajs/react' 97 | 98 | router.post('/users', data, { 99 | preserveScroll: (page) => page.props.someProp === 'value', 100 | }) 101 | ``` 102 | 103 | ```js Svelte icon="s" 104 | import { router } from '@inertiajs/svelte' 105 | 106 | router.post('/users', data, { 107 | preserveScroll: (page) => page.props.someProp === 'value', 108 | }) 109 | ``` 110 | 111 | 112 | 113 | When using an [Inertia link](/v1/the-basics/links), you can preserve the scroll position using the `preserveScroll` prop. 114 | 115 | 116 | 117 | ```vue Vue 2 icon="vuejs" 118 | import { Link } from '@inertiajs/vue2' 119 | 120 | Home 121 | ``` 122 | 123 | ```vue Vue 3 icon="vuejs" 124 | import { Link } from '@inertiajs/vue3' 125 | 126 | Home 127 | ``` 128 | 129 | ```jsx React icon="react" 130 | import { Link } from '@inertiajs/react' 131 | 132 | Home 133 | ``` 134 | 135 | ```svelte Svelte icon="s" 136 | import { inertia, Link } from '@inertiajs/svelte' 137 | 138 | Home 139 | 140 | Home 141 | ``` 142 | 143 | 144 | 145 | ## Scroll Regions 146 | 147 | If your app doesn't use document body scrolling, but instead has scrollable elements (using the `overflow` CSS property), scroll resetting will not work. 148 | 149 | In these situations, you must tell Inertia which scrollable elements to manage by adding the `scroll-region` attribute to the element. 150 | 151 | ```html 152 |
153 | 154 |
155 | ``` 156 | -------------------------------------------------------------------------------- /v1/advanced/csrf-protection.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: CSRF Protection 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | ## Making Requests 8 | 9 | Laravel automatically includes the proper CSRF token when making requests via Inertia or Axios. However, if you're using Laravel, be sure to omit the `csrf-token` meta tag from your project, as this will prevent the CSRF token from refreshing properly. 10 | 11 | If your server-side framework includes cross-site request forgery (CSRF) protection, you'll need to ensure that each Inertia requests includes the necessary CSRF token for `POST`, `PUT`, `PATCH`, and `DELETE` requests. 12 | 13 | Of course, as already discussed, some server-side frameworks such as Laravel automatically handle the inclusion of the CSRF token when making requests. **Therefore, no additional configuration is required when using one of these frameworks.** 14 | 15 | However, if you need to handle CSRF protection manually, one approach is to include the CSRF token as a prop on every response. You can then use the token when making Inertia requests. 16 | 17 | 18 | 19 | ```js Vue 2 icon="vuejs" 20 | import { router } from '@inertiajs/vue2' 21 | 22 | router.post('/users', { 23 | _token: this.$page.props.csrf_token, 24 | name: 'John Doe', 25 | email: 'john.doe@example.com', 26 | }) 27 | ``` 28 | 29 | ```js Vue 3 icon="vuejs" 30 | import { router, usePage } from '@inertiajs/vue3' 31 | 32 | const page = usePage() 33 | 34 | router.post('/users', { 35 | _token: page.props.csrf_token, 36 | name: 'John Doe', 37 | email: 'john.doe@example.com', 38 | }) 39 | ``` 40 | 41 | ```js React icon="react" 42 | import { router, usePage } from '@inertiajs/react' 43 | 44 | const props = usePage().props 45 | 46 | router.post('/users', { 47 | _token: props.csrf_token, 48 | name: 'John Doe', 49 | email: 'john.doe@example.com', 50 | }) 51 | ``` 52 | 53 | ```js Svelte icon="s" 54 | import { page, router } from '@inertiajs/svelte' 55 | 56 | router.post('/users', { 57 | _token: $page.props.csrf_token, 58 | name: 'John Doe', 59 | email: 'john.doe@example.com', 60 | }) 61 | ``` 62 | 63 | 64 | 65 | You can even use Inertia's [shared data](/v1/the-basics/shared-data) functionality to automatically include the `csrf_token` with each response. 66 | 67 | However, a better approach is to use the CSRF functionality already built into [axios](https://github.com/axios/axios) for this. Axios is the HTTP library that Inertia uses under the hood. 68 | 69 | Axios automatically checks for the existence of an `XSRF-TOKEN` cookie. If it's present, it will then include the token in an `X-XSRF-TOKEN` header for any requests it makes. 70 | 71 | The easiest way to implement this is using server-side middleware. Simply include the `XSRF-TOKEN`cookie on each response, and then verify the token using the `X-XSRF-TOKEN` header sent in the requests from axios. 72 | 73 | ## Handling Mismatches 74 | 75 | When a CSRF token mismatch occurs, your server-side framework will likely throw an exception that results in an error response. For example, when using Laravel, a `TokenMismatchException` is thrown which results in a `419` error page. Since that isn't a valid Inertia response, the error is shown in a modal. 76 | 77 | 78 | 79 | Obviously, this isn't a great user experience. A better way to handle these errors is to return a redirect back to the previous page, along with a flash message that the page expired. This will result in a valid Inertia response with the flash message available as a prop which you can then display to the user. Of course, you'll need to share your [flash messages](/shared-data#flash-messages) with Inertia for this to work. 80 | 81 | When using Laravel, you may modify your application's exception handler to automatically redirect the user back to the page they were previously on while flashing a message to the session. To accomplish this, you may use the `respond` exception method in your application's `bootstrap/app.php` file. 82 | 83 | ```php 84 | use Symfony\Component\HttpFoundation\Response; 85 | 86 | ->withExceptions(function (Exceptions $exceptions) { 87 | $exceptions->respond(function (Response $response) { 88 | if ($response->getStatusCode() === 419) { 89 | return back()->with([ 90 | 'message' => 'The page expired, please try again.', 91 | ]); 92 | } 93 | 94 | return $response; 95 | }); 96 | }); 97 | ``` 98 | 99 | The end result is a much better experience for your users. Instead of seeing the error modal, the user is instead presented with a message that the page "expired" and are asked to try again. 100 | 101 | 102 | -------------------------------------------------------------------------------- /v2/data-props/shared-data.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Shared Data 3 | --- 4 | 5 | Sometimes you need to access specific pieces of data on numerous pages within your application. For example, you may need to display the current user in the site header. Passing this data manually in each response across your entire application is cumbersome. Thankfully, there is a better option: shared data. 6 | 7 | ## Sharing Data 8 | 9 | Inertia's server-side adapters all provide a method of making shared data available for every request. This is typically done outside of your controllers. Shared data will be automatically merged with the page props provided in your controller. 10 | 11 | In Laravel applications, this is typically handled by the `HandleInertiaRequests` middleware that is automatically installed when installing the [server-side adapter](/v2/installation/server-side-setup#middleware). 12 | 13 | ```php 14 | class HandleInertiaRequests extends Middleware 15 | { 16 | public function share(Request $request) 17 | { 18 | return array_merge(parent::share($request), [ 19 | // Synchronously... 20 | 'appName' => config('app.name'), 21 | 22 | // Lazily... 23 | 'auth.user' => fn () => $request->user() 24 | ? $request->user()->only('id', 'name', 'email') 25 | : null, 26 | ]); 27 | } 28 | } 29 | ``` 30 | 31 | Alternatively, you can manually share data using the `Inertia::share` method. 32 | 33 | ```php 34 | use Inertia\Inertia; 35 | 36 | // Synchronously... 37 | Inertia::share('appName', config('app.name')); 38 | 39 | // Lazily... 40 | Inertia::share('user', fn (Request $request) => $request->user() 41 | ? $request->user()->only('id', 'name', 'email') 42 | : null 43 | ); 44 | ``` 45 | 46 | Shared data should be used sparingly as all shared data is included with every response. 47 | 48 | Page props and shared data are merged together, so be sure to namespace your shared data appropriately to avoid collisions. 49 | 50 | ## Sharing Once Props 51 | 52 | You may share data that is resolved only once and remembered by the client across subsequent navigations using [once props](/v2/data-props/once-props). 53 | 54 | ```php 55 | class HandleInertiaRequests extends Middleware 56 | { 57 | public function share(Request $request) 58 | { 59 | return array_merge(parent::share($request), [ 60 | 'countries' => Inertia::once(fn () => Country::all()), 61 | ]); 62 | } 63 | } 64 | ``` 65 | 66 | Alternatively, you may define a dedicated `shareOnce()` method in the middleware. The middleware will evaluate both `share()` and `shareOnce()`, merging the results. 67 | 68 | ```php 69 | class HandleInertiaRequests extends Middleware 70 | { 71 | public function shareOnce(Request $request): array 72 | { 73 | return array_merge(parent::shareOnce($request), [ 74 | 'countries' => fn () => Country::all(), 75 | ]); 76 | } 77 | } 78 | ``` 79 | 80 | You may also share once props manually using the `Inertia::shareOnce()` method. 81 | 82 | ```php 83 | Inertia::shareOnce('countries', fn () => Country::all()); 84 | ``` 85 | 86 | ## Accessing Shared Data 87 | 88 | Once you have shared the data server-side, you will be able to access it within any of your pages or components. Here's an example of how to access shared data in a layout component. 89 | 90 | 91 | 92 | ```vue Vue icon="vuejs" 93 | 101 | 102 | 112 | ``` 113 | 114 | ```jsx React icon="react" 115 | import { usePage } from '@inertiajs/react' 116 | 117 | export default function Layout({ children }) { 118 | const { auth } = usePage().props 119 | 120 | return ( 121 |
122 |
123 | You are logged in as: {auth.user.name} 124 |
125 |
126 | {children} 127 |
128 |
129 | ) 130 | } 131 | ``` 132 | 133 | ```svelte Svelte icon="s" 134 | 137 | 138 |
139 |
140 | You are logged in as: {$page.props.auth.user.name} 141 |
142 |
143 | 144 |
145 |
146 | ``` 147 | 148 |
149 | 150 | ## Flash Data 151 | 152 | For one-time notifications like toast messages or success alerts, you may use [flash data](/v2/data-props/flash-data). Unlike shared data, flash data is not persisted in the browser's history state, so it won't reappear when navigating through history. 153 | -------------------------------------------------------------------------------- /v2/installation/server-side-setup.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Server-Side Setup 3 | --- 4 | 5 | The first step when installing Inertia is to configure your server-side framework. Inertia maintains an official server-side adapter for [Laravel](https://laravel.com/). For other frameworks, please see the [community adapters](/v2/installation/community-adapters). 6 | 7 | Inertia is fine-tuned for Laravel, so the documentation examples on this website utilize Laravel. For examples of using Inertia with other server-side frameworks, please refer to the framework specific documentation maintained by that adapter. 8 | 9 | 16 | Laravel's starter kits provide out-of-the-box scaffolding for new Inertia applications. 17 | 18 | These starter kits are the absolute fastest way to start building a new Inertia project using Laravel and Vue or React. However, if you would like to manually install Inertia into your application, please consult the documentation below. 19 | 20 | 21 | ## Installation 22 | 23 | 24 | 25 | First, install the Inertia server-side adapter using the Composer package manager. 26 | 27 | ```bash 28 | composer require inertiajs/inertia-laravel 29 | ``` 30 | 31 | 32 | 33 | Next, setup the root template that will be loaded on the first page visit to your application. This template should include your site's CSS and JavaScript assets, along with the `@inertia` and `@inertiaHead` directives. 34 | 35 | ```blade 36 | 37 | 38 | 39 | 40 | @vite('resources/js/app.js') 41 | @inertiaHead 42 | 43 | 44 | @inertia 45 | 46 | 47 | ``` 48 | 49 | For React applications, it's recommended to include the `@viteReactRefresh` directive before the `@vite` directive to enable Fast Refresh in development. 50 | 51 | The `@inertia` directive renders a `
` element with an `id` of `app`. This element serves as the mounting point for your JavaScript application. You may customize the `id` by passing a different value to the directive. 52 | 53 | ```blade 54 | 55 | ... 56 | 57 | @inertia('custom-app-id') 58 | 59 | 60 | ``` 61 | 62 | If you change the `id` of the root element, be sure to update it [client-side](/v2/installation/client-side-setup#defining-a-root-element) as well. 63 | 64 | By default, Inertia's Laravel adapter will assume your root template is named `app.blade.php`. If you would like to use a different root view, you can change it using the `Inertia::setRootView()` method. 65 | 66 | 67 | 68 | Next we need to setup the Inertia middleware. You can accomplish this by publishing the `HandleInertiaRequests` middleware to your application, which can be done using the following Artisan command. 69 | 70 | ```sh 71 | php artisan inertia:middleware 72 | ``` 73 | 74 | Once the middleware has been published, append the `HandleInertiaRequests` middleware to the `web` middleware group in your application's `bootstrap/app.php` file. 75 | 76 | ```php 77 | use App\Http\Middleware\HandleInertiaRequests; 78 | 79 | ->withMiddleware(function (Middleware $middleware) { 80 | $middleware->web(append: [ 81 | HandleInertiaRequests::class, 82 | ]); 83 | }) 84 | ``` 85 | 86 | This middleware provides a `version()` method for setting your [asset version](/v2/advanced/asset-versioning), as well as a `share()` method for defining [shared data](/v2/data-props/shared-data). 87 | 88 | 89 | 90 | That's it, you're all ready to go server-side! Now you're ready to start creating Inertia [pages](/v2/the-basics/pages) and rendering them via [responses](/v2/the-basics/responses). 91 | 92 | ```php 93 | use Inertia\Inertia; 94 | 95 | class EventsController extends Controller 96 | { 97 | public function show(Event $event) 98 | { 99 | return Inertia::render('Event/Show', [ 100 | 'event' => $event->only( 101 | 'id', 102 | 'title', 103 | 'start_date', 104 | 'description', 105 | ), 106 | ]); 107 | } 108 | } 109 | ``` 110 | 111 | 112 | -------------------------------------------------------------------------------- /v2/data-props/once-props.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Once Props 3 | --- 4 | 5 | Some data rarely changes, is expensive to compute, or is simply large. Rather than including this data in every response, you may use _once props_. These props are remembered by the client and reused on subsequent pages that include the same prop. This makes them ideal for [shared data](/v2/data-props/shared-data). 6 | 7 | ## Creating Once Props 8 | 9 | To create a once prop, use the `Inertia::once()` method when returning your response. This method receives a callback that returns the prop data. 10 | 11 | ```php 12 | return Inertia::render('Billing', [ 13 | 'plans' => Inertia::once(fn () => Plan::all()), 14 | ]); 15 | ``` 16 | 17 | After the client has received this prop, subsequent requests will skip resolving the callback and exclude the prop from the response. The client only remembers once props while navigating between pages that include them. 18 | 19 | Navigating to a page without the once prop will forget the remembered value, and it will be resolved again on the next page that has it. In practice, this is rarely an issue since once props are typically used as shared data or within a specific section of your application. 20 | 21 | ## Forcing a Refresh 22 | 23 | You may force a once prop to be refreshed using the `fresh()` method. 24 | 25 | ```php 26 | return Inertia::render('Billing', [ 27 | 'plans' => Inertia::once(fn () => Plan::all())->fresh(), 28 | ]); 29 | ``` 30 | 31 | This method also accepts a boolean, allowing you to conditionally refresh the prop. 32 | 33 | ```php 34 | return Inertia::render('Billing', [ 35 | 'plans' => Inertia::once(fn () => Plan::all())->fresh($condition), 36 | ]); 37 | ``` 38 | 39 | ## Refreshing from the Client 40 | 41 | You may refresh a once prop from the client-side using a [partial reload](/v2/data-props/partial-reloads). The server will always resolve a once prop when explicitly requested. 42 | 43 | 44 | 45 | ```js Vue icon="vuejs" 46 | import { router } from '@inertiajs/vue3' 47 | 48 | router.reload({ only: ['plans'] }) 49 | ``` 50 | 51 | ```js React icon="react" 52 | import { router } from '@inertiajs/react' 53 | 54 | router.reload({ only: ['plans'] }) 55 | ``` 56 | 57 | ```js Svelte icon="s" 58 | import { router } from '@inertiajs/svelte' 59 | 60 | router.reload({ only: ['plans'] }) 61 | ``` 62 | 63 | 64 | 65 | ## Expiration 66 | 67 | You may set an expiration time using the `until()` method. This method accepts a `DateTimeInterface`, `DateInterval`, or an integer (seconds). The prop will be refreshed on a subsequent visit after the expiration time has passed. 68 | 69 | ```php 70 | return Inertia::render('Dashboard', [ 71 | 'rates' => Inertia::once(fn () => ExchangeRate::all())->until(now()->addDay()), 72 | ]); 73 | ``` 74 | 75 | ## Custom Keys 76 | 77 | You may assign a custom key to the prop using the `as()` method. This is useful when you want to share data across multiple pages while using different prop names. 78 | 79 | ```php 80 | // Team member list... 81 | return Inertia::render('Team/Index', [ 82 | 'memberRoles' => Inertia::once(fn () => Role::all())->as('roles'), 83 | ]); 84 | 85 | // Invite form... 86 | return Inertia::render('Team/Invite', [ 87 | 'availableRoles' => Inertia::once(fn () => Role::all())->as('roles'), 88 | ]); 89 | ``` 90 | 91 | Both pages share the same underlying data because they use the same custom key, so the prop is only resolved for whichever page you visit first. 92 | 93 | ## Sharing Once Props 94 | 95 | You may share once props globally using the `Inertia::share()` method. 96 | 97 | ```php 98 | Inertia::share('countries', Inertia::once(fn () => Country::all())); 99 | ``` 100 | 101 | Or, for convenience, you may use the `shareOnce()` method. 102 | 103 | ```php 104 | Inertia::shareOnce('countries', fn () => Country::all()); 105 | ``` 106 | 107 | You may also chain `as()`, `fresh()`, and `until()` onto the `shareOnce` method. 108 | 109 | ```php 110 | Inertia::shareOnce('countries', fn () => Country::all())->until(now()->addDay()); 111 | ``` 112 | 113 | Additionally, you may define a dedicated `shareOnce()` method in your middleware. The middleware will evaluate both `share()` and `shareOnce()`, merging the results. 114 | 115 | ```php 116 | class HandleInertiaRequests extends Middleware 117 | { 118 | public function shareOnce(Request $request): array 119 | { 120 | return array_merge(parent::shareOnce($request), [ 121 | 'countries' => fn () => Country::all(), 122 | ]); 123 | } 124 | } 125 | ``` 126 | 127 | ## Prefetching 128 | 129 | Once props are compatible with [prefetching](/v2/data-props/prefetching). The client automatically includes any remembered once props in prefetched responses, so navigating to a prefetched page will already have the once props available. 130 | 131 | Prefetched pages containing an expired once prop will be invalidated from the cache. 132 | 133 | ## Combining with Other Prop Types 134 | 135 | The `once()` modifier may be chained onto [deferred](/v2/data-props/deferred-props), [merge](/v2/data-props/merging-props), and [optional](/v2/data-props/partial-reloads#lazy-data-evaluation) props. 136 | 137 | ```php 138 | return Inertia::render('Dashboard', [ 139 | 'permissions' => Inertia::defer(fn () => Permission::all())->once(), 140 | 'activity' => Inertia::merge(fn () => $user->recentActivity())->once(), 141 | 'categories' => Inertia::optional(fn () => Category::all())->once(), 142 | ]); 143 | ``` 144 | -------------------------------------------------------------------------------- /snippets/color-generator.jsx: -------------------------------------------------------------------------------- 1 | export const ColorGenerator = () => { 2 | const [hue, setHue] = useState(180); 3 | const [saturation, setSaturation] = useState(50); 4 | const [lightness, setLightness] = useState(50); 5 | const [colors, setColors] = useState([]); 6 | 7 | useEffect(() => { 8 | const newColors = []; 9 | for (let i = 0; i < 5; i++) { 10 | const l = Math.max(10, Math.min(90, lightness - 20 + i * 10)); 11 | newColors.push(`hsl(${hue}, ${saturation}%, ${l}%)`); 12 | } 13 | setColors(newColors); 14 | }, [hue, saturation, lightness]); 15 | 16 | const copyToClipboard = (color) => { 17 | navigator.clipboard 18 | .writeText(color) 19 | .then(() => { 20 | console.log(`Copied ${color} to clipboard!`); 21 | }) 22 | .catch((err) => { 23 | console.error("Failed to copy: ", err); 24 | }); 25 | }; 26 | 27 | return ( 28 |
29 |
30 |
31 | 54 | 55 | 74 | 75 | 94 |
95 | 96 |
97 | {colors.map((color, idx) => ( 98 |
copyToClipboard(color)} 104 | /> 105 | ))} 106 |
107 | 108 |
109 |

110 | Base color: hsl({hue}, {saturation}%, {lightness}%) 111 |

112 |
113 |
114 |
115 | ); 116 | }; 117 | -------------------------------------------------------------------------------- /v1/installation/client-side-setup.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Client-side Setup 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | Once you have your [server-side framework configured](/v1/installation/server-side-setup), you then need to setup your client-side framework. Inertia currently provides support for React, Vue, and Svelte. 8 | 9 | ## Laravel Starter Kits 10 | 11 | Laravel's [starter kits](https://laravel.com/docs/starter-kits), Breeze and Jetstream, provide out-of-the-box scaffolding for new Inertia applications. These starter kits are the absolute fastest way to start building a new Inertia project using Laravel and Vue or React. However, if you would like to manually install Inertia into your application, please consult the documentation below. 12 | 13 | ## Install Dependencies 14 | 15 | First, install the Inertia client-side adapter corresponding to your framework of choice. 16 | 17 | 18 | 19 | ```bash Vue 2 20 | npm install @inertiajs/vue2 21 | ``` 22 | 23 | ```bash Vue 3 24 | npm install @inertiajs/vue3 25 | ``` 26 | 27 | ```bash React icon="react" 28 | npm install @inertiajs/react 29 | ``` 30 | 31 | ```bash Svelte icon="s" 32 | npm install @inertiajs/svelte 33 | ``` 34 | 35 | 36 | 37 | ## Initialize the Inertia App 38 | 39 | Next, update your main JavaScript file to boot your Inertia app. To accomplish this, we'll initialize the client-side framework with the base Inertia component. 40 | 41 | 42 | 43 | ```js Vue 2 icon="vuejs" 44 | import Vue from 'vue' 45 | import { createInertiaApp } from '@inertiajs/vue2' 46 | 47 | createInertiaApp({ 48 | resolve: name => { 49 | const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) 50 | return pages[`./Pages/${name}.vue`] 51 | }, 52 | setup({ el, App, props, plugin }) { 53 | Vue.use(plugin) 54 | 55 | new Vue({ 56 | render: h => h(App, props), 57 | }).$mount(el) 58 | }, 59 | }) 60 | ``` 61 | 62 | ```js Vue 3 icon="vuejs" 63 | import { createApp, h } from 'vue' 64 | import { createInertiaApp } from '@inertiajs/vue3' 65 | 66 | createInertiaApp({ 67 | resolve: name => { 68 | const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) 69 | return pages[`./Pages/${name}.vue`] 70 | }, 71 | setup({ el, App, props, plugin }) { 72 | createApp({ render: () => h(App, props) }) 73 | .use(plugin) 74 | .mount(el) 75 | }, 76 | }) 77 | ``` 78 | 79 | ```jsx React icon="react" 80 | import { createInertiaApp } from '@inertiajs/react' 81 | import { createRoot } from 'react-dom/client' 82 | 83 | createInertiaApp({ 84 | resolve: name => { 85 | const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true }) 86 | return pages[`./Pages/${name}.jsx`] 87 | }, 88 | setup({ el, App, props }) { 89 | createRoot(el).render() 90 | }, 91 | }) 92 | ``` 93 | 94 | ```js Svelte icon="s" 95 | import { createInertiaApp } from '@inertiajs/svelte' 96 | 97 | createInertiaApp({ 98 | resolve: name => { 99 | const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true }) 100 | return pages[`./Pages/${name}.svelte`] 101 | }, 102 | setup({ el, App, props }) { 103 | new App({ target: el, props }) 104 | }, 105 | }) 106 | ``` 107 | 108 | 109 | 110 | The `setup` callback receives everything necessary to initialize the client-side framework, including the root Inertia `App` component. 111 | 112 | ## Resolving Components 113 | 114 | The `resolve` callback tells Inertia how to load a page component. It receives a page name (string), and returns a page component module. How you implement this callback depends on which bundler (Vite or Webpack) you're using. 115 | 116 | 117 | 118 | ```js Vue 2 icon="vuejs" 119 | // Vite 120 | resolve: name => { 121 | const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) 122 | return pages[`./Pages/${name}.vue`] 123 | }, 124 | 125 | // Webpack 126 | resolve: name => require(`./Pages/${name}`), 127 | ``` 128 | 129 | ```js Vue 3 icon="vuejs" 130 | // Vite 131 | resolve: name => { 132 | const pages = import.meta.glob('./Pages/**/*.vue', { eager: true }) 133 | return pages[`./Pages/${name}.vue`] 134 | }, 135 | 136 | // Webpack 137 | resolve: name => require(`./Pages/${name}`), 138 | ``` 139 | 140 | ```js React icon="react" 141 | // Vite 142 | resolve: name => { 143 | const pages = import.meta.glob('./Pages/**/*.jsx', { eager: true }) 144 | return pages[`./Pages/${name}.jsx`] 145 | }, 146 | 147 | // Webpack 148 | resolve: name => require(`./Pages/${name}`), 149 | ``` 150 | 151 | ```js Svelte icon="s" 152 | // Vite 153 | resolve: name => { 154 | const pages = import.meta.glob('./Pages/**/*.svelte', { eager: true }) 155 | return pages[`./Pages/${name}.svelte`] 156 | }, 157 | 158 | // Webpack 159 | resolve: name => require(`./Pages/${name}.svelte`), 160 | ``` 161 | 162 | 163 | 164 | By default we recommend eager loading your components, which will result in a single JavaScript bundle. However, if you'd like to lazy-load your components, see our [code splitting](/code-splitting) documentation. 165 | 166 | ## Defining a Root Element 167 | 168 | By default, Inertia assumes that your application's root template has a root element with an `id` of `app`. If your application's root element has a different `id`, you can provide it using the `id` property. 169 | 170 | ```js 171 | createInertiaApp({ 172 | id: 'my-app', 173 | // ... 174 | }) 175 | ``` 176 | -------------------------------------------------------------------------------- /v2/data-props/deferred-props.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Deferred Props 3 | --- 4 | 5 | Inertia's deferred props feature allows you to defer the loading of certain page data until after the initial page render. This can be useful for improving the perceived performance of your app by allowing the initial page render to happen as quickly as possible. 6 | 7 | ## Server Side 8 | 9 | To defer a prop, you can use the `Inertia::defer()` method when returning your response. This method receives a callback that returns the prop data. The callback will be executed in a separate request after the initial page render. 10 | 11 | ```php 12 | Route::get('/users', function () { 13 | return Inertia::render('Users/Index', [ 14 | 'users' => User::all(), 15 | 'roles' => Role::all(), 16 | 'permissions' => Inertia::defer(fn () => Permission::all()), 17 | ]); 18 | }); 19 | ``` 20 | 21 | ### Grouping Requests 22 | 23 | By default, all deferred props get fetched in one request after the initial page is rendered, but you can choose to fetch data in parallel by grouping props together. 24 | 25 | ```php 26 | Route::get('/users', function () { 27 | return Inertia::render('Users/Index', [ 28 | 'users' => User::all(), 29 | 'roles' => Role::all(), 30 | 'permissions' => Inertia::defer(fn () => Permission::all()), 31 | 'teams' => Inertia::defer(fn () => Team::all(), 'attributes'), 32 | 'projects' => Inertia::defer(fn () => Project::all(), 'attributes'), 33 | 'tasks' => Inertia::defer(fn () => Task::all(), 'attributes'), 34 | ]); 35 | }); 36 | ``` 37 | 38 | In the example above, the `teams`, `projects`, and `tasks` props will be fetched in one request, while the `permissions` prop will be fetched in a separate request in parallel. Group names are arbitrary strings and can be anything you choose. 39 | 40 | ## Client Side 41 | 42 | On the client side, Inertia provides the `Deferred` component to help you manage deferred props. This component will automatically wait for the specified deferred props to be available before rendering its children. 43 | 44 | 45 | 46 | ```vue Vue icon="vuejs" 47 | 50 | 51 | 62 | ``` 63 | 64 | ```jsx React icon="react" 65 | import { Deferred } from '@inertiajs/react' 66 | 67 | export default () => ( 68 | Loading...
}> 69 | 70 | 71 | ) 72 | ``` 73 | 74 | ```svelte Svelte 4 icon="s" 75 | 80 | 81 | 82 | 83 |
Loading...
84 |
85 | 86 | {#each permissions as permission} 87 | 88 | {/each} 89 |
90 | ``` 91 | 92 | ```svelte Svelte 5 icon="s" 93 | 98 | 99 | 100 | {#snippet fallback()} 101 |
Loading...
102 | {/snippet} 103 | 104 | {#each permissions as permission} 105 | 106 | {/each} 107 |
108 | ``` 109 | 110 | 111 | 112 | ## Multiple Deferred Props 113 | 114 | If you need to wait for multiple deferred props to become available, you can specify an array to the `data` prop. 115 | 116 | 117 | 118 | ```vue Vue icon="vuejs" 119 | 122 | 123 | 132 | ``` 133 | 134 | ```jsx React icon="react" 135 | import { Deferred } from '@inertiajs/react' 136 | 137 | export default () => ( 138 | Loading...
}> 139 | 140 | 141 | ) 142 | ``` 143 | 144 | ```svelte Svelte 4 icon="s" 145 | 151 | 152 | 153 | 154 |
Loading...
155 |
156 | 157 | 158 |
159 | ``` 160 | 161 | ```svelte Svelte 5 icon="s" 162 | 167 | 168 | 169 | {#snippet fallback()} 170 |
Loading...
171 | {/snippet} 172 | 173 | 174 |
175 | ``` 176 | 177 | 178 | 179 | ## Combining with Once Props 180 | 181 | You may chain the `once()` modifier onto a deferred prop to ensure the data is resolved only once and remembered by the client across subsequent navigations. 182 | 183 | ```php 184 | return Inertia::render('Dashboard', [ 185 | 'stats' => Inertia::defer(fn () => Stats::generate())->once(), 186 | ]); 187 | ``` 188 | 189 | For more information on once props, see the [once props](/v2/data-props/once-props) documentation. 190 | -------------------------------------------------------------------------------- /v2/data-props/merging-props.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Merging Props 3 | --- 4 | 5 | Inertia overwrites props with the same name when reloading a page. However, you may need to merge new data with existing data instead. For example, when implementing a "load more" button for paginated results. The [Infinite scroll](/v2/data-props/infinite-scroll) component uses prop merging under the hood. 6 | 7 | Prop merging only works during [partial reloads](/v2/data-props/partial-reloads). Full page visits will always replace props entirely, even if you've marked them for merging. 8 | 9 | ## Merge Methods 10 | 11 | To merge a prop instead of overwriting it, you may use the `Inertia::merge()` method when returning your response. 12 | 13 | ```php 14 | Route::get('/items', function () { 15 | // Static array of tags... 16 | $allTags = [ 17 | 'Laravel', 'React', 'Vue', 'Tailwind', 'Inertia', 18 | 'PHP', 'JavaScript', 'TypeScript', 'Docker', 'Vite', 19 | ]; 20 | 21 | // Get chunk of tags by page... 22 | $page = request()->input('page', 1); 23 | $perPage = 5; 24 | $offset = ($page - 1) * $perPage; 25 | $tags = array_slice($allTags, $offset, $perPage); 26 | 27 | return Inertia::render('Tags/Index', [ 28 | 'tags' => Inertia::merge($tags), 29 | ]); 30 | }); 31 | ``` 32 | 33 | The `Inertia::merge()` method will append new items to existing arrays at the root level. You may change this behavior to prepend items instead. 34 | 35 | ```php 36 | // Append at root level (default)... 37 | Inertia::merge($items); 38 | 39 | // Prepend at root level... 40 | Inertia::merge($items)->prepend(); 41 | ``` 42 | 43 | For more precise control, you can target specific nested properties for merging while replacing the rest of the object. 44 | 45 | ```php 46 | // Only append to the 'data' array, replace everything else... 47 | Inertia::merge(User::paginate())->append('data'); 48 | 49 | // Prepend to the 'messages' array... 50 | Inertia::merge($chatData)->prepend('messages'); 51 | ``` 52 | 53 | You can combine multiple operations and target several properties at once. 54 | 55 | ```php 56 | Inertia::merge($forumData) 57 | ->append('posts') 58 | ->prepend('announcements'); 59 | 60 | // Target multiple properties... 61 | Inertia::merge($dashboardData) 62 | ->append(['notifications', 'activities']); 63 | ``` 64 | 65 | On the client side, Inertia handles all the merging automatically according to your server-side configuration. 66 | 67 | ## Matching Items 68 | 69 | When merging arrays, you may use the `matchOn` parameter to match existing items by a specific field and update them instead of appending new ones. 70 | 71 | ```php 72 | // Match posts by ID, update existing ones... 73 | Inertia::merge($postData)->append('data', matchOn: 'id'); 74 | 75 | // Multiple properties with different match fields... 76 | Inertia::merge($complexData)->append([ 77 | 'users.data' => 'id', 78 | 'messages' => 'uuid', 79 | ]); 80 | ``` 81 | 82 | In the first example, Inertia will iterate over the `data` array and attempt to match each item by its `id` field. If a match is found, the existing item will be replaced. If no match is found, the new item will be appended. 83 | 84 | ## Deep Merge 85 | 86 | Instead of specifying which nested paths should be merged, you may use `Inertia::deepMerge()`to ensure a deep merge of the entire structure. 87 | 88 | ```php 89 | Route::get('/chat', function () { 90 | $chatData = [ 91 | 'messages' => [ 92 | ['id' => 4, 'text' => 'Hello there!', 'user' => 'Alice'], 93 | ['id' => 5, 'text' => 'How are you?', 'user' => 'Bob'], 94 | ], 95 | 'online' => 12, 96 | ]; 97 | 98 | return Inertia::render('Chat', [ 99 | 'chat' => Inertia::deepMerge($chatData)->matchOn('messages.id'), 100 | ]); 101 | }); 102 | ``` 103 | 104 | `Inertia::deepMerge()` was introduced before `Inertia::merge()` had support for prepending and targeting nested paths. In most cases, `Inertia::merge()` with its append and prepend methods should be sufficient. 105 | 106 | ## Client Side Visits 107 | 108 | You can also merge props directly on the client side without making a server request using [client side visits](/v2/the-basics/manual-visits#client-side-visits). Inertia provides [prop helper methods](/v2/the-basics/manual-visits#prop-helpers) that allow you to append, prepend, or replace prop values. 109 | 110 | ## Combining with Deferred Props 111 | 112 | You may combine [deferred props](/v2/data-props/deferred-props) with mergeable props to defer the loading of the prop and ultimately mark it as mergeable once it's loaded. 113 | 114 | ```php 115 | Route::get('/users', function () { 116 | $page = request()->input('page', 1); 117 | $perPage = request()->input('per_page', 10); 118 | 119 | return Inertia::render('Users/Index', [ 120 | 'results' => Inertia::defer(fn() => User::paginate($perPage, page: $page))->deepMerge(), 121 | ]); 122 | }); 123 | ``` 124 | 125 | ## Combining with Once Props 126 | 127 | You may chain the `once()` modifier onto a merge prop to ensure the data is resolved only once and remembered by the client across subsequent navigations. 128 | 129 | ```php 130 | return Inertia::render('Users/Index', [ 131 | 'activity' => Inertia::merge(fn () => $user->recentActivity())->once(), 132 | ]); 133 | ``` 134 | 135 | For more information on once props, see the [once props](/v2/data-props/once-props) documentation. 136 | 137 | ## Resetting Props 138 | 139 | On the client side, you can indicate to the server that you would like to reset the prop. This is useful when you want to clear the prop value before merging new data, such as when the user enters a new search query on a paginated list. 140 | 141 | The `reset` request option accepts an array of the props keys you would like to reset. 142 | 143 | ```js 144 | router.reload({ 145 | reset: ['results'], 146 | // ... 147 | }) 148 | ``` 149 | -------------------------------------------------------------------------------- /v2/data-props/remembering-state.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Remembering State 3 | --- 4 | 5 | When navigating browser history, Inertia restores pages using prop data cached in history state. However, Inertia does not restore local page component state since this is beyond its reach. This can lead to outdated pages in your browser history. 6 | 7 | For example, if a user partially completes a form, then navigates away, and then returns back, the form will be reset and their work will be lost. 8 | 9 | To mitigate this issue, you can tell Inertia which local component state to save in the browser history. 10 | 11 | ## Saving Local State 12 | 13 | To save local component state to the history state, use the `useRemember` feature to tell Inertia which data it should remember. 14 | 15 | 16 | 17 | ```js Vue icon="vuejs" 18 | import { useRemember } from '@inertiajs/vue3' 19 | 20 | const form = useRemember({ 21 | first_name: null, 22 | last_name: null, 23 | }) 24 | ``` 25 | 26 | ```jsx React icon="react" 27 | import { useRemember } from '@inertiajs/react' 28 | 29 | export default function Profile() { 30 | const [formState, setFormState] = useRemember({ 31 | first_name: null, 32 | last_name: null, 33 | // ... 34 | }) 35 | 36 | // ... 37 | } 38 | ``` 39 | 40 | ```js Svelte icon="s" 41 | import { useRemember } from '@inertiajs/svelte' 42 | 43 | const form = useRemember({ 44 | first_name: null, 45 | last_name: null, 46 | }) 47 | 48 | // ... 49 | ``` 50 | 51 | 52 | 53 | Now, whenever your local `form` state changes, Inertia will automatically save this data to the history state and will also restore it on history navigation. 54 | 55 | ## Multiple Components 56 | 57 | If your page contains multiple components that use the remember functionality provided by Inertia, you need to provide a unique key for each component so that Inertia knows which data to restore to each component. 58 | 59 | 60 | 61 | ```js Vue icon="vuejs" 62 | import { useRemember } from '@inertiajs/vue3' 63 | 64 | const form = useRemember({ 65 | first_name: null, 66 | last_name: null, 67 | }, 'Users/Create') 68 | ``` 69 | 70 | ```jsx React icon="react" 71 | import { useRemember } from '@inertiajs/react' 72 | 73 | export default function Profile() { 74 | const [formState, setFormState] = useRemember({ 75 | first_name: null, 76 | last_name: null, 77 | }, 'Users/Create') 78 | } 79 | ``` 80 | 81 | ```js Svelte icon="s" 82 | import { page, useRemember } from '@inertiajs/svelte' 83 | 84 | const form = useRemember({ 85 | first_name: null, 86 | last_name: null, 87 | }, 'Users/Create') 88 | ``` 89 | 90 | 91 | 92 | If you have multiple instances of the same component on the page using the remember functionality, be sure to also include a unique key for each component instance, such as a model identifier. 93 | 94 | 95 | 96 | ```js Vue icon="vuejs" 97 | import { useRemember } from '@inertiajs/vue3' 98 | 99 | const props = defineProps({ user: Object }) 100 | 101 | const form = useRemember({ 102 | first_name: null, 103 | last_name: null, 104 | }, `Users/Edit:${props.user.id}`) 105 | ``` 106 | 107 | ```jsx React icon="react" 108 | import { useRemember } from '@inertiajs/react' 109 | 110 | export default function Profile() { 111 | const [formState, setFormState] = useRemember({ 112 | first_name: props.user.first_name, 113 | last_name: props.user.last_name, 114 | }, `Users/Edit:${this.user.id}`) 115 | } 116 | ``` 117 | 118 | ```js Svelte icon="s" 119 | import { page, useRemember } from '@inertiajs/svelte' 120 | 121 | const form = useRemember({ 122 | first_name: $page.props.user.first_name, 123 | last_name: $page.props.user.last_name, 124 | }, `Users/Edit:${$page.props.user.id}`) 125 | ``` 126 | 127 | 128 | 129 | ## Form Helper 130 | 131 | If you're using the [Inertia form helper](/v2/the-basics/forms#form-helper), you can pass a unique form key as the first argument when instantiating your form. This will cause the form data and errors to automatically be remembered. 132 | 133 | 134 | 135 | ```js Vue icon="vuejs" 136 | import { useForm } from '@inertiajs/vue3' 137 | 138 | const form = useForm('CreateUser', data) 139 | const form = useForm(`EditUser:${props.user.id}`, data) 140 | ``` 141 | 142 | ```js React icon="react" 143 | import { useForm } from '@inertiajs/react' 144 | 145 | const form = useForm('CreateUser', data) 146 | const form = useForm(`EditUser:${user.id}`, data) 147 | ``` 148 | 149 | ```js Svelte icon="s" 150 | import { useForm } from '@inertiajs/svelte' 151 | 152 | const form = useForm('CreateUser', data) 153 | const form = useForm(`EditUser:${user.id}`, data) 154 | ``` 155 | 156 | 157 | 158 | ## Manually Saving State 159 | 160 | The `useRemember` hook watches for data changes and automatically saves those changes to the history state. Then, Inertia will restore the data on page load. 161 | 162 | However, it's also possible to manage this manually using the underlying `remember()` and `restore()` methods exposed by Inertia. 163 | 164 | 165 | 166 | ```js Vue icon="vuejs" 167 | import { router } from '@inertiajs/vue3' 168 | 169 | // Save local component state to history state 170 | router.remember(data, 'my-key') 171 | 172 | // Restore local component state from history state 173 | let data = router.restore('my-key') 174 | ``` 175 | 176 | ```js React icon="react" 177 | import { router } from '@inertiajs/react' 178 | 179 | // Save local component state to history state 180 | router.remember(data, 'my-key') 181 | 182 | // Restore local component state from history state 183 | let data = router.restore('my-key') 184 | ``` 185 | 186 | ```js Svelte icon="s" 187 | import { router } from '@inertiajs/svelte' 188 | 189 | // Save local component state to history state 190 | router.remember(data, 'my-key') 191 | 192 | // Restore local component state from history state 193 | let data = router.restore('my-key') 194 | ``` 195 | 196 | 197 | -------------------------------------------------------------------------------- /v2/the-basics/file-uploads.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: File Uploads 3 | --- 4 | 5 | ## `FormData` Conversion 6 | 7 | When making Inertia requests that include files (even nested files), Inertia will automatically convert the request data into a `FormData` object. This conversion is necessary in order to submit a `multipart/form-data` request via XHR. 8 | 9 | If you would like the request to always use a `FormData` object regardless of whether a file is present in the data, you may provide the `forceFormData` option when making the request. 10 | 11 | 12 | 13 | ```js Vue icon="vuejs" 14 | import { router } from '@inertiajs/vue3' 15 | 16 | router.post('/users', data, { 17 | forceFormData: true, 18 | }) 19 | ``` 20 | 21 | ```js React icon="react" 22 | import { router } from '@inertiajs/react' 23 | 24 | router.post('/users', data, { 25 | forceFormData: true, 26 | }) 27 | ``` 28 | 29 | ```js Svelte icon="s" 30 | import { router } from '@inertiajs/svelte' 31 | 32 | router.post('/users', data, { 33 | forceFormData: true, 34 | }) 35 | ``` 36 | 37 | 38 | 39 | You can learn more about the `FormData` interface via its [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/FormData). 40 | 41 | ## File Upload Example 42 | 43 | Let's examine a complete file upload example using Inertia. This example includes both a `name` text input and an `avatar` file input. 44 | 45 | 46 | 47 | ```vue Vue icon="vuejs" 48 | 60 | 61 | 71 | ``` 72 | 73 | ```jsx React icon="react" 74 | import { useForm } from '@inertiajs/react' 75 | 76 | const { data, setData, post, progress } = useForm({ 77 | name: null, 78 | avatar: null, 79 | }) 80 | 81 | function submit(e) { 82 | e.preventDefault() 83 | post('/users') 84 | } 85 | 86 | return ( 87 |
88 | setData('name', e.target.value)} /> 89 | setData('avatar', e.target.files[0])} /> 90 | {progress && ( 91 | 92 | {progress.percentage}% 93 | 94 | )} 95 | 96 |
97 | ) 98 | ``` 99 | 100 | ```svelte Svelte 4 icon="s" 101 | 113 | 114 |
115 | 116 | $form.avatar = e.target.files[0]} /> 117 | {#if $form.progress} 118 | 119 | {$form.progress.percentage}% 120 | 121 | {/if} 122 | 123 |
124 | ``` 125 | 126 | ```svelte Svelte 5 icon="s" 127 | 140 | 141 |
142 | 143 | $form.avatar = e.target.files[0]} /> 144 | {#if $form.progress} 145 | 146 | {$form.progress.percentage}% 147 | 148 | {/if} 149 | 150 |
151 | ``` 152 | 153 |
154 | 155 | This example uses the [Inertia form helper](/v2/the-basics/forms#form-helper) for convenience, since the form helper provides easy access to the current upload progress. However, you are free to submit your forms using [manual Inertia visits](/v2/the-basics/manual-visits) as well. 156 | 157 | ## Multipart Limitations 158 | 159 | Uploading files using a `multipart/form-data` request is not natively supported in some server-side frameworks when using the `PUT`,`PATCH`, or `DELETE` HTTP methods. The simplest workaround for this limitation is to simply upload files using a `POST` request instead. 160 | 161 | However, some frameworks, such as [Laravel](https://laravel.com/docs/routing#form-method-spoofing) and [Rails ](https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark), support form method spoofing, which allows you to upload the files using `POST`, but have the framework handle the request as a `PUT` or `PATCH` request. This is done by including a `_method` attribute in the data of your request. 162 | 163 | 164 | 165 | ```js Vue icon="vuejs" 166 | import { router } from '@inertiajs/vue3' 167 | 168 | router.post(`/users/${user.id}`, { 169 | _method: 'put', 170 | avatar: form.avatar, 171 | }) 172 | ``` 173 | 174 | ```js React icon="react" 175 | import { router } from '@inertiajs/react' 176 | 177 | router.post(`/users/${user.id}`, { 178 | _method: 'put', 179 | avatar: form.avatar, 180 | }) 181 | ``` 182 | 183 | ```js Svelte icon="s" 184 | import { router } from '@inertiajs/svelte' 185 | 186 | router.post(`/users/${user.id}`, { 187 | _method: 'put', 188 | avatar: form.avatar, 189 | }) 190 | ``` 191 | 192 | 193 | -------------------------------------------------------------------------------- /v1/advanced/error-handling.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Error Handling 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | ## Development 8 | 9 | One of the advantages to working with a robust server-side framework is the built-in exception handling you get for free. For example, Laravel ships with [Ignition](https://github.com/facade/ignition), a beautiful error reporting tool which displays a nicely formatted stack trace in local development. 10 | 11 | The challenge is, if you're making an XHR request (which Inertia does) and you hit a server-side error, you're typically left digging through the network tab in your browser's devtools to diagnose the problem. 12 | 13 | Inertia solves this issue by showing all non-Inertia responses in a modal. This means you get the same beautiful error-reporting you're accustomed to, even though you've made that request over XHR. 14 | 15 | ## Production 16 | 17 | In production you will want to return a proper Inertia error response instead of relying on the modal-driven error reporting that is present during development. To accomplish this, you'll need to update your framework's default exception handler to return a custom error page. 18 | 19 | When building Laravel applications, you can accomplish this by using the `respond` exception method in your application's `bootstrap/app.php` file. 20 | 21 | ```php 22 | use Illuminate\Http\Request; 23 | use Symfony\Component\HttpFoundation\Response; 24 | use Inertia\Inertia; 25 | 26 | ->withExceptions(function (Exceptions $exceptions) { 27 | $exceptions->respond(function (Response $response, Throwable $exception, Request $request) { 28 | if (! app()->environment(['local', 'testing']) && in_array($response->getStatusCode(), [500, 503, 404, 403])) { 29 | return Inertia::render('Error', ['status' => $response->getStatusCode()]) 30 | ->toResponse($request) 31 | ->setStatusCode($response->getStatusCode()); 32 | } elseif ($response->getStatusCode() === 419) { 33 | return back()->with([ 34 | 'message' => 'The page expired, please try again.', 35 | ]); 36 | } 37 | 38 | return $response; 39 | }); 40 | }) 41 | ``` 42 | 43 | You may have noticed we're returning an `Error` page component in the example above. You'll need to actually create this component, which will serve as the generic error page for your application. Here's an example error component you can use as a starting point. 44 | 45 | 46 | 47 | ```vue Vue 2 icon="vuejs" 48 | 54 | 55 | 80 | ``` 81 | 82 | ```vue Vue 3 icon="vuejs" 83 | 106 | 107 | 113 | ``` 114 | 115 | ```jsx React icon="react" 116 | export default function ErrorPage({ status }) { 117 | const title = { 118 | 503: '503: Service Unavailable', 119 | 500: '500: Server Error', 120 | 404: '404: Page Not Found', 121 | 403: '403: Forbidden', 122 | }[status] 123 | 124 | const description = { 125 | 503: 'Sorry, we are doing some maintenance. Please check back soon.', 126 | 500: 'Whoops, something went wrong on our servers.', 127 | 404: 'Sorry, the page you are looking for could not be found.', 128 | 403: 'Sorry, you are forbidden from accessing this page.', 129 | }[status] 130 | 131 | return ( 132 |
133 |

{title}

134 |
{description}
135 |
136 | ) 137 | } 138 | ``` 139 | 140 | ```html Svelte icon="s" 141 | 158 | 159 |
160 |

{title}

161 |
{description}
162 |
163 | ``` 164 | 165 |
166 | -------------------------------------------------------------------------------- /v2/advanced/error-handling.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Error Handling 3 | --- 4 | 5 | ## Development 6 | 7 | One of the advantages to working with a robust server-side framework is the built-in exception handling you get for free. For example, Laravel ships with a beautiful error reporting tool which displays a nicely formatted stack trace in local development. 8 | 9 | The challenge is, if you're making an XHR request (which Inertia does) and you hit a server-side error, you're typically left digging through the network tab in your browser's devtools to diagnose the problem. 10 | 11 | Inertia solves this issue by showing all non-Inertia responses in a modal. This means you get the same beautiful error-reporting you're accustomed to, even though you've made that request over XHR. 12 | 13 | ## Dialog Element 14 | 15 | By default, Inertia displays error modals using a custom `
` overlay. However, you can opt-in to using the native HTML `` element instead, which provides built-in modal functionality including backdrop handling. 16 | 17 | To enable this, configure the `future.useDialogForErrorModal` option in your [application defaults](/v2/installation/client-side-setup#configuring-defaults). 18 | 19 | ```js 20 | createInertiaApp({ 21 | // resolve, setup, etc. 22 | defaults: { 23 | future: { 24 | useDialogForErrorModal: true, 25 | }, 26 | }, 27 | }) 28 | ``` 29 | 30 | ## Production 31 | 32 | In production you will want to return a proper Inertia error response instead of relying on the modal-driven error reporting that is present during development. To accomplish this, you'll need to update your framework's default exception handler to return a custom error page. 33 | 34 | When building Laravel applications, you can accomplish this by using the `respond` exception method in your application's `bootstrap/app.php` file. 35 | 36 | ```php 37 | use Illuminate\Http\Request; 38 | use Symfony\Component\HttpFoundation\Response; 39 | use Inertia\Inertia; 40 | 41 | ->withExceptions(function (Exceptions $exceptions) { 42 | $exceptions->respond(function (Response $response, Throwable $exception, Request $request) { 43 | if (! app()->environment(['local', 'testing']) && in_array($response->getStatusCode(), [500, 503, 404, 403])) { 44 | return Inertia::render('ErrorPage', ['status' => $response->getStatusCode()]) 45 | ->toResponse($request) 46 | ->setStatusCode($response->getStatusCode()); 47 | } 48 | 49 | if ($response->getStatusCode() === 419) { 50 | return back()->with([ 51 | 'message' => 'The page expired, please try again.', 52 | ]); 53 | } 54 | 55 | return $response; 56 | }); 57 | }) 58 | ``` 59 | 60 | You may have noticed we're returning an `ErrorPage` page component in the example above. You'll need to actually create this component, which will serve as the generic error page for your application. Here's an example error component you can use as a starting point. 61 | 62 | 63 | 64 | ```vue Vue icon="vuejs" 65 | 88 | 89 | 95 | ``` 96 | 97 | ```jsx React icon="react" 98 | export default function ErrorPage({ status }) { 99 | const title = { 100 | 503: '503: Service Unavailable', 101 | 500: '500: Server Error', 102 | 404: '404: Page Not Found', 103 | 403: '403: Forbidden', 104 | }[status] 105 | 106 | const description = { 107 | 503: 'Sorry, we are doing some maintenance. Please check back soon.', 108 | 500: 'Whoops, something went wrong on our servers.', 109 | 404: 'Sorry, the page you are looking for could not be found.', 110 | 403: 'Sorry, you are forbidden from accessing this page.', 111 | }[status] 112 | 113 | return ( 114 |
115 |

{title}

116 |
{description}
117 |
118 | ) 119 | } 120 | ``` 121 | 122 | ```svelte Svelte 4 icon="s" 123 | 140 | 141 |
142 |

{title}

143 |
{description}
144 |
145 | ``` 146 | 147 | ```svelte Svelte 5 icon="s" 148 | 165 | 166 |
167 |

{title[status]}

168 |
{description[status]}
169 |
170 | ``` 171 | 172 |
173 | -------------------------------------------------------------------------------- /v1/advanced/testing.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Testing 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | There are many different ways to test an Inertia application. This page provides a quick overview of the tools available. 8 | 9 | ## End-to-end Tests 10 | 11 | One popular approach to testing your JavaScript page components is to use an end-to-end testing tool like [Cypress](https://www.cypress.io/) or [Laravel Dusk](https://laravel.com/docs/dusk). These are browser automation tools that allow you to run real simulations of your app in the browser. These tests are known to be slower; however, since they test your application at the same layer as your end users, they can provide a lot of confidence that your app is working correctly. And, since these tests are run in the browser, your JavaScript code is actually executed and tested as well. 12 | 13 | ## Client-side Unit Tests 14 | 15 | Another approach to testing your page components is using a client-side unit testing framework, such as [Jest](https://jestjs.io/) or [Mocha](https://mochajs.org/). This approach allows you to test your JavaScript page components in isolation using Node.js. 16 | 17 | ## Endpoint Tests 18 | 19 | In addition to testing your JavaScript page components, you will likely want to also test the Inertia responses that are returned by your server-side framework. A popular approach to doing this is using endpoint tests, where you make requests to your application and examine the responses. Laravel [provides tooling](https://laravel.com/docs/http-tests) for executing these types of tests. 20 | 21 | However, to make this process even easier, Inertia's Laravel adapter provides additional HTTP testing tools. Let's take a look at an example. 22 | 23 | ```php 24 | use Inertia\Testing\AssertableInertia as Assert; 25 | class PodcastsControllerTest extends TestCase 26 | { 27 | public function test_can_view_podcast() 28 | { 29 | $this->get('/podcasts/41') 30 | ->assertInertia(fn (Assert $page) => $page 31 | ->component('Podcasts/Show') 32 | ->has('podcast', fn (Assert $page) => $page 33 | ->where('id', $podcast->id) 34 | ->where('subject', 'The Laravel Podcast') 35 | ->where('description', 'The Laravel Podcast brings you Laravel and PHP development news and discussion.') 36 | ->has('seasons', 4) 37 | ->has('seasons.4.episodes', 21) 38 | ->has('host', fn (Assert $page) => $page 39 | ->where('id', 1) 40 | ->where('name', 'Matt Stauffer') 41 | ) 42 | ->has('subscribers', 7, fn (Assert $page) => $page 43 | ->where('id', 2) 44 | ->where('name', 'Claudio Dekker') 45 | ->where('platform', 'Apple Podcasts') 46 | ->etc() 47 | ->missing('email') 48 | ->missing('password') 49 | ) 50 | ) 51 | ); 52 | } 53 | } 54 | ``` 55 | 56 | As you can see in the example above, you may use these assertion methods to assert against the content of the data provided to the Inertia response. In addition, you may assert that array data has a given length as well as scope your assertions. 57 | 58 | Let's dig into the available assertions in detail. First, to assert that the Inertia response has a property, you may use the `has` method. You can think of this method as being similar to PHP's `isset` function. 59 | 60 | ```php 61 | $response->assertInertia(fn (Assert $page) => $page 62 | // Checking a root-level property... 63 | ->has('podcast') 64 | // Checking nested properties using "dot" notation... 65 | ->has('podcast.id') 66 | ); 67 | ``` 68 | 69 | To assert that an Inertia property has a specified amount of items, you may provide the expected size as the second argument to the `has` method. 70 | 71 | ```php 72 | $response->assertInertia(fn (Assert $page) => $page 73 | // Checking if a root-level property has 7 items... 74 | ->has('podcasts', 7) 75 | // Checking nested properties using "dot" notation... 76 | ->has('podcast.subscribers', 7) 77 | ); 78 | ``` 79 | 80 | The `has` method may also be used to scope properties in order to lessen repetition when asserting against nested properties. 81 | 82 | ```php 83 | $response->assertInertia(fn (Assert $page) => $page 84 | // Creating a single-level property scope... 85 | ->has('message', fn (Assert $page) => $page 86 | // We can now continue chaining methods... 87 | ->has('subject') 88 | ->has('comments', 5) 89 | // And can even create a deeper scope using "dot" notation... 90 | ->has('comments.0', fn (Assert $page) => $page 91 | ->has('body') 92 | ->has('files', 1) 93 | ->has('files.0', fn (Assert $page) => $page 94 | ->has('url') 95 | ) 96 | ) 97 | ) 98 | ); 99 | ``` 100 | 101 | When scoping into Inertia properties that are arrays or collections, you may also assert that a specified number of items are present in addition to scoping into the first item. 102 | 103 | ```php 104 | $response->assertInertia(fn (Assert $page) => $page 105 | // Assert that there are 5 comments and automatically scope into the first comment... 106 | ->has('comments', 5, fn (Assert $page) => $page 107 | ->has('body') 108 | // ... 109 | ) 110 | ); 111 | ``` 112 | 113 | To assert that an Inertia property has an expected value, you may use the `where` assertion. 114 | 115 | ```php 116 | $response->assertInertia(fn (Assert $page) => $page 117 | ->has('message', fn (Assert $page) => $page 118 | // Assert that the subject prop matches the given message... 119 | ->where('subject', 'This is an example message') 120 | // Or, assert against deeply nested values... 121 | ->where('comments.0.files.0.name', 'example-attachment.pdf') 122 | ) 123 | ); 124 | ``` 125 | 126 | Inertia's testing methods will automatically fail when you haven't interacted with at least one of the props in a scope. While this is generally useful, you might run into situations where you're working with unreliable data (such as from an external feed), or with data that you really don't want interact with in order to keep your test simple. For these situations, the `etc` method exists. 127 | 128 | ```php 129 | $response->assertInertia(fn (Assert $page) => $page 130 | ->has('message', fn (Assert $page) => $page 131 | ->has('subject') 132 | ->has('comments') 133 | ->etc() 134 | ) 135 | ); 136 | ``` 137 | 138 | The `missing` method is the exact opposite of the `has` method, ensuring that the property does not exist. This method makes a great companion to the `etc` method. 139 | 140 | ```php 141 | $response->assertInertia(fn (Assert $page) => $page 142 | ->has('message', fn (Assert $page) => $page 143 | ->has('subject') 144 | ->missing('published_at') 145 | ->etc() 146 | ) 147 | ); 148 | ``` 149 | -------------------------------------------------------------------------------- /v1/the-basics/file-uploads.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: File Uploads 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | ## Formdata Conversion 8 | 9 | When making Inertia requests that include files (even nested files), Inertia will automatically convert the request data into a `FormData` object. This conversion is necessary in order to submit a `multipart/form-data` request via XHR. 10 | 11 | If you would like the request to always use a `FormData` object regardless of whether a file is present in the data, you may provide the `forceFormData` option when making the request. 12 | 13 | 14 | 15 | ```js Vue 2 icon="vuejs" 16 | import { router } from '@inertiajs/vue2' 17 | 18 | router.post('/users', data, { 19 | forceFormData: true, 20 | }) 21 | ``` 22 | 23 | ```js Vue 3 icon="vuejs" 24 | import { router } from '@inertiajs/vue3' 25 | 26 | router.post('/users', data, { 27 | forceFormData: true, 28 | }) 29 | ``` 30 | 31 | ```js React icon="react" 32 | import { router } from '@inertiajs/react' 33 | 34 | router.post('/users', data, { 35 | forceFormData: true, 36 | }) 37 | ``` 38 | 39 | ```js Svelte icon="s" 40 | import { router } from '@inertiajs/svelte' 41 | 42 | router.post('/users', data, { 43 | forceFormData: true, 44 | }) 45 | ``` 46 | 47 | 48 | 49 | You can learn more about the `FormData` interface via its [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/API/FormData). 50 | 51 | Prior to version 0.8.0, Inertia did not automatically convert requests to `FormData`. If you're using an Inertia release prior to this version, you will need to manually perform this conversion. 52 | 53 | ## File Upload Example 54 | 55 | Let's examine a complete file upload example using Inertia. This example includes both a `name` text input and an `avatar` file input. 56 | 57 | 58 | 59 | ```vue Vue 2 icon="vuejs" 60 | 70 | 71 | 90 | ``` 91 | 92 | ```vue Vue 3 icon="vuejs" 93 | 105 | 106 | 116 | ``` 117 | 118 | ```jsx React icon="react" 119 | import { useForm } from '@inertiajs/react' 120 | 121 | const { data, setData, post, progress } = useForm({ 122 | name: null, 123 | avatar: null, 124 | }) 125 | 126 | function submit(e) { 127 | e.preventDefault() 128 | post('/users') 129 | } 130 | 131 | return ( 132 |
133 | setData('name', e.target.value)} /> 134 | setData('avatar', e.target.files[0])} /> 135 | {progress && ( 136 | 137 | {progress.percentage}% 138 | 139 | )} 140 | 141 |
142 | ) 143 | ``` 144 | 145 | ```html Svelte icon="s" 146 | 158 | 159 |
160 | 161 | $form.avatar = e.target.files[0]} /> 162 | {#if $form.progress} 163 | 164 | {$form.progress.percentage}% 165 | 166 | {/if} 167 | 168 |
169 | ``` 170 | 171 |
172 | 173 | This example uses the [Inertia form helper](/forms#form-helper) for convenience, since the form helper provides easy access to the current upload progress. However, you are free to submit your forms using [manual Inertia visits](/v1/the-basics/manual-visits) as well. 174 | 175 | ## Multipart Limitations 176 | 177 | Uploading files using a `multipart/form-data` request is not natively supported in some server-side frameworks when using the `PUT`,`PATCH`, or `DELETE` HTTP methods. The simplest workaround for this limitation is to simply upload files using a `POST` request instead. 178 | 179 | However, some frameworks, such as [Laravel](https://laravel.com/docs/8.x/routing#form-method-spoofing) and [Rails ](https://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark), support form method spoofing, which allows you to upload the files using `POST`, but have the framework handle the request as a `PUT` or `PATCH` request. This is done by including a `_method` attribute in the data of your request. 180 | 181 | 182 | 183 | ```js Vue 2 icon="vuejs" 184 | import { router } from '@inertiajs/vue2' 185 | 186 | router.post(`/users/${user.id}\ 187 | ``` 188 | 189 | ```js Vue 3 icon="vuejs" 190 | import { router } from '@inertiajs/vue3' 191 | 192 | router.post(`/users/${user.id}\ 193 | ``` 194 | 195 | ```js React icon="react" 196 | import { router } from '@inertiajs/react' 197 | 198 | router.post(`/users/${user.id}\ 199 | ``` 200 | 201 | ```js Svelte icon="s" 202 | import { router } from '@inertiajs/svelte' 203 | 204 | router.post(`/users/${user.id}\ 205 | ``` 206 | 207 | 208 | -------------------------------------------------------------------------------- /v1/the-basics/shared-data.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Shared Data 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | Sometimes you need to access specific pieces of data on numerous pages within your application. For example, you may need to display the current user in the site header. Passing this data manually in each response across your entire application is cumbersome. Thankfully, there is a better option: shared data. 8 | 9 | ## Sharing Data 10 | 11 | Inertia's server-side adapters all provide a method of making shared data available for every request. This is typically done outside of your controllers. Shared data will be automatically merged with the page props provided in your controller. 12 | 13 | In Laravel applications, this is typically handled by the `HandleInertiaRequests` middleware that is automatically installed when installing the [server-side adapter](/server-side-setup#middleware). 14 | 15 | ```php 16 | class HandleInertiaRequests extends Middleware 17 | { 18 | public function share(Request $request) 19 | { 20 | return array_merge(parent::share($request), [ 21 | // Synchronously... 22 | 'appName' => config('app.name'), 23 | 24 | // Lazily... 25 | 'auth.user' => fn () => $request->user() 26 | ? $request->user()->only('id', 'name', 'email') 27 | : null, 28 | ]); 29 | } 30 | } 31 | ``` 32 | 33 | Alternatively, you can manually share data using the `Inertia::share` method. 34 | 35 | ```php 36 | use Inertia\Inertia; 37 | 38 | // Synchronously... 39 | Inertia::share('appName', config('app.name')); 40 | 41 | // Lazily... 42 | Inertia::share('user', fn (Request $request) => $request->user() 43 | ? $request->user()->only('id', 'name', 'email') 44 | : null 45 | ); 46 | ``` 47 | 48 | Shared data should be used sparingly as all shared data is included with every response. 49 | 50 | Page props and shared data are merged together, so be sure to namespace your shared data appropriately to avoid collisions. 51 | 52 | ## Accessing Shared Data 53 | 54 | Once you have shared the data server-side, you will be able to access it within any of your pages or components. Here's an example of how to access shared data in a layout component. 55 | 56 | 57 | 58 | ```vue Vue 2 icon="vuejs" 59 | 69 | 70 | 79 | ``` 80 | 81 | ```vue Vue 3 icon="vuejs" 82 | 90 | 91 | 101 | ``` 102 | 103 | ```jsx React icon="react" 104 | import { usePage } from '@inertiajs/react' 105 | 106 | export default function Layout({ children }) { 107 | const { auth } = usePage().props 108 | 109 | return ( 110 |
111 |
112 | You are logged in as: {auth.user.name} 113 |
114 |
115 | {children} 116 |
117 |
118 | ) 119 | } 120 | ``` 121 | 122 | ```html Svelte icon="s" 123 | 126 | 127 |
128 |
129 | You are logged in as: {$page.props.auth.user.name} 130 |
131 |
132 | 133 |
134 |
135 | ``` 136 | 137 |
138 | 139 | ## Flash Messages 140 | 141 | Another great use-case for shared data is flash messages. These are messages stored in the session only for the next request. For example, it's common to set a flash message after completing a task and before redirecting to a different page. 142 | 143 | Here's a simple way to implement flash messages in your Inertia applications. First, share the flash message on each request. 144 | 145 | ```php 146 | class HandleInertiaRequests extends Middleware 147 | { 148 | public function share(Request $request) 149 | { 150 | return array_merge(parent::share($request), [ 151 | 'flash' => [ 152 | 'message' => fn () => $request->session()->get('message') 153 | ], 154 | ]); 155 | } 156 | } 157 | ``` 158 | 159 | Next, display the flash message in a frontend component, such as the site layout. 160 | 161 | 162 | 163 | ```vue Vue 2 icon="vuejs" 164 | 176 | ``` 177 | 178 | ```vue Vue 3 icon="vuejs" 179 | 191 | ``` 192 | 193 | ```jsx React icon="react" 194 | import { usePage } from '@inertiajs/react' 195 | 196 | export default function Layout({ children }) { 197 | const { flash } = usePage().props 198 | 199 | return ( 200 |
201 |
202 |
203 | {flash.message && ( 204 |
{flash.message}
205 | )} 206 | {children} 207 |
208 |
209 |
210 | ) 211 | } 212 | ``` 213 | 214 | ```html Svelte icon="s" 215 | 218 | 219 |
220 |
221 |
222 | {#if $page.props.flash.message} 223 |
{$page.props.flash.message}
224 | {/if} 225 | 226 |
227 |
228 |
229 | ``` 230 | 231 |
232 | -------------------------------------------------------------------------------- /v1/advanced/partial-reloads.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Partial Reloads 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | When making visits to the same page you are already on, it's not always necessary to re-fetch all of the page's data from the server. In fact, selecting only a subset of the data can be a helpful performance optimization if it's acceptable that some page data becomes stale. Inertia makes this possible via its "partial reload" feature. 8 | 9 | As an example, consider a "user index" page that includes a list of users, as well as an option to filter the users by their company. On the first request to the page, both the `users` and `companies`props are passed to the page component. However, on subsequent visits to the same page (maybe to filter the users), you can request only the `users` data from the server without requesting the `companies` data. Inertia will then automatically merge the partial data returned from the server with the data it already has in memory client-side. 10 | 11 | Partial reloads only work for visits made to the same page component. 12 | 13 | ## Only Certain Props 14 | 15 | To perform a partial reload, use the `only` visit option to specify which data the server should return. This option should be an array of keys which correspond to the keys of the props. 16 | 17 | 18 | 19 | ```js Vue 2 icon="vuejs" 20 | import { router } from '@inertiajs/vue2' 21 | 22 | router.visit(url, { 23 | only: ['users'], 24 | }) 25 | ``` 26 | 27 | ```js Vue 3 icon="vuejs" 28 | import { router } from '@inertiajs/vue3' 29 | 30 | router.visit(url, { 31 | only: ['users'], 32 | }) 33 | ``` 34 | 35 | ```js React icon="react" 36 | import { router } from '@inertiajs/react' 37 | 38 | router.visit(url, { 39 | only: ['users'], 40 | }) 41 | ``` 42 | 43 | ```js Svelte icon="s" 44 | import { router } from '@inertiajs/svelte' 45 | 46 | router.visit(url, { 47 | only: ['users'], 48 | }) 49 | ``` 50 | 51 | 52 | 53 | ## Except Certain Props 54 | 55 | In addition to the `only` visit option you can also use the `except` option to specify which data the server should exclude. This option should also be an array of keys which correspond to the keys of the props. 56 | 57 | 58 | 59 | ```js Vue 2 icon="vuejs" 60 | import { router } from '@inertiajs/vue2' 61 | 62 | router.visit(url, { 63 | except: ['users'], 64 | }) 65 | ``` 66 | 67 | ```js Vue 3 icon="vuejs" 68 | import { router } from '@inertiajs/vue3' 69 | 70 | router.visit(url, { 71 | except: ['users'], 72 | }) 73 | ``` 74 | 75 | ```js React icon="react" 76 | import { router } from '@inertiajs/react' 77 | 78 | router.visit(url, { 79 | except: ['users'], 80 | }) 81 | ``` 82 | 83 | ```js Svelte icon="s" 84 | import { router } from '@inertiajs/svelte' 85 | 86 | router.visit(url, { 87 | except: ['users'], 88 | }) 89 | ``` 90 | 91 | 92 | 93 | ## Router Shorthand 94 | 95 | Since partial reloads can only be made to the same page component the user is already on, it almost always makes sense to just use the `router.reload()` method, which automatically uses the current URL. 96 | 97 | 98 | 99 | ```js Vue 2 icon="vuejs" 100 | import { router } from '@inertiajs/vue2' 101 | 102 | router.reload({ only: ['users'] }) 103 | ``` 104 | 105 | ```js Vue 3 icon="vuejs" 106 | import { router } from '@inertiajs/vue3' 107 | 108 | router.reload({ only: ['users'] }) 109 | ``` 110 | 111 | ```js React icon="react" 112 | import { router } from '@inertiajs/react' 113 | 114 | router.reload({ only: ['users'] }) 115 | ``` 116 | 117 | ```js Svelte icon="s" 118 | import { router } from '@inertiajs/svelte' 119 | 120 | router.reload({ only: ['users'] }) 121 | ``` 122 | 123 | 124 | 125 | ## Using Links 126 | 127 | It's also possible to perform partial reloads with Inertia links using the `only` property. 128 | 129 | 130 | 131 | ```vue Vue 2 icon="vuejs" 132 | import { Link } from '@inertiajs/vue2' 133 | 134 | Show active 135 | ``` 136 | 137 | ```vue Vue 3 icon="vuejs" 138 | import { Link } from '@inertiajs/vue3' 139 | 140 | Show active 141 | ``` 142 | 143 | ```jsx React icon="react" 144 | import { Link } from '@inertiajs/react' 145 | 146 | Show active 147 | ``` 148 | 149 | ```svelte Svelte icon="s" 150 | import { inertia, Link } from '@inertiajs/svelte' 151 | 152 | Show active 153 | 154 | Show active 155 | ``` 156 | 157 | 158 | 159 | ## Lazy Data Evaluation 160 | 161 | For partial reloads to be most effective, be sure to also use lazy data evaluation when returning props from your server-side routes or controllers. This can be accomplished by wrapping all optional page data in a closure. 162 | 163 | ```php 164 | return Inertia::render('Users/Index', [ 165 | 'users' => fn () => User::all(), 166 | 'companies' => fn () => Company::all(), 167 | ]); 168 | ``` 169 | 170 | When Inertia performs a request, it will determine which data is required and only then will it evaluate the closure. This can significantly increase the performance of pages that contain a lot of optional data. 171 | 172 | Additionally, Inertia provides an `Inertia::lazy()` method to specify that a prop should never be included unless explicitly requested using the `only` option: 173 | 174 | ```php 175 | return Inertia::render('Users/Index', [ 176 | 'users' => Inertia::lazy(fn () => User::all()), 177 | ]); 178 | ``` 179 | 180 | On the inverse, you can use the `Inertia::always()` method to specify that a prop should always be included, even if it has not been explicitly required in a partial reload. 181 | 182 | ```php 183 | return Inertia::render('Users/Index', [ 184 | 'users' => Inertia::always(User::all()), 185 | ]); 186 | ``` 187 | 188 | Here's a summary of each approach: 189 | 190 | ```php 191 | return Inertia::render('Users/Index', [ 192 | // ALWAYS included on standard visits 193 | // OPTIONALLY included on partial reloads 194 | // ALWAYS evaluated 195 | 'users' => User::all(), 196 | 197 | // ALWAYS included on standard visits 198 | // OPTIONALLY included on partial reloads 199 | // ONLY evaluated when needed 200 | 'users' => fn () => User::all(), 201 | 202 | // NEVER included on standard visits 203 | // OPTIONALLY included on partial reloads 204 | // ONLY evaluated when needed 205 | 'users' => Inertia::lazy(fn () => User::all()), 206 | 207 | // ALWAYS included on standard visits 208 | // ALWAYS included on partial reloads 209 | // ALWAYS evaluated 210 | 'users' => Inertia::always(User::all()), 211 | ]); 212 | ``` 213 | -------------------------------------------------------------------------------- /v2/data-props/partial-reloads.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Partial Reloads 3 | --- 4 | 5 | When making visits to the same page you are already on, it's not always necessary to re-fetch all of the page's data from the server. In fact, selecting only a subset of the data can be a helpful performance optimization if it's acceptable that some page data becomes stale. Inertia makes this possible via its "partial reload" feature. 6 | 7 | As an example, consider a "user index" page that includes a list of users, as well as an option to filter the users by their company. On the first request to the page, both the `users` and `companies`props are passed to the page component. However, on subsequent visits to the same page (maybe to filter the users), you can request only the `users` data from the server without requesting the `companies` data. Inertia will then automatically merge the partial data returned from the server with the data it already has in memory client-side. 8 | 9 | Partial reloads only work for visits made to the same page component. 10 | 11 | ## Only Certain Props 12 | 13 | To perform a partial reload, use the `only` visit option to specify which data the server should return. This option should be an array of keys which correspond to the keys of the props. 14 | 15 | 16 | 17 | ```js Vue icon="vuejs" 18 | import { router } from '@inertiajs/vue3' 19 | 20 | router.visit(url, { 21 | only: ['users'], 22 | }) 23 | ``` 24 | 25 | ```js React icon="react" 26 | import { router } from '@inertiajs/react' 27 | 28 | router.visit(url, { 29 | only: ['users'], 30 | }) 31 | ``` 32 | 33 | ```js Svelte icon="s" 34 | import { router } from '@inertiajs/svelte' 35 | 36 | router.visit(url, { 37 | only: ['users'], 38 | }) 39 | ``` 40 | 41 | 42 | 43 | ## Except Certain Props 44 | 45 | In addition to the `only` visit option you can also use the `except` option to specify which data the server should exclude. This option should also be an array of keys which correspond to the keys of the props. 46 | 47 | 48 | 49 | ```js Vue icon="vuejs" 50 | import { router } from '@inertiajs/vue3' 51 | 52 | router.visit(url, { 53 | except: ['users'], 54 | }) 55 | ``` 56 | 57 | ```js React icon="react" 58 | import { router } from '@inertiajs/react' 59 | 60 | router.visit(url, { 61 | except: ['users'], 62 | }) 63 | ``` 64 | 65 | ```js Svelte icon="s" 66 | import { router } from '@inertiajs/svelte' 67 | 68 | router.visit(url, { 69 | except: ['users'], 70 | }) 71 | ``` 72 | 73 | 74 | 75 | ## Router Shorthand 76 | 77 | Since partial reloads can only be made to the same page component the user is already on, it almost always makes sense to just use the `router.reload()` method, which automatically uses the current URL. 78 | 79 | 80 | 81 | ```js Vue icon="vuejs" 82 | import { router } from '@inertiajs/vue3' 83 | 84 | router.reload({ only: ['users'] }) 85 | ``` 86 | 87 | ```js React icon="react" 88 | import { router } from '@inertiajs/react' 89 | 90 | router.reload({ only: ['users'] }) 91 | ``` 92 | 93 | ```js Svelte icon="s" 94 | import { router } from '@inertiajs/svelte' 95 | 96 | router.reload({ only: ['users'] }) 97 | ``` 98 | 99 | 100 | 101 | ## Using Links 102 | 103 | It's also possible to perform partial reloads with Inertia links using the `only` property. 104 | 105 | 106 | 107 | ```vue Vue icon="vuejs" 108 | import { Link } from '@inertiajs/vue3' 109 | 110 | Show active 111 | ``` 112 | 113 | ```jsx React icon="react" 114 | import { Link } from '@inertiajs/react' 115 | 116 | Show active 117 | ``` 118 | 119 | ```svelte Svelte icon="s" 120 | import { inertia, Link } from '@inertiajs/svelte' 121 | 122 | Show active 123 | 124 | Show active 125 | ``` 126 | 127 | 128 | 129 | ## Lazy Data Evaluation 130 | 131 | For partial reloads to be most effective, be sure to also use lazy data evaluation when returning props from your server-side routes or controllers. This can be accomplished by wrapping all optional page data in a closure. 132 | 133 | ```php 134 | return Inertia::render('Users/Index', [ 135 | 'users' => fn () => User::all(), 136 | 'companies' => fn () => Company::all(), 137 | ]); 138 | ``` 139 | 140 | When Inertia performs a request, it will determine which data is required and only then will it evaluate the closure. This can significantly increase the performance of pages that contain a lot of optional data. 141 | 142 | Additionally, Inertia provides an `Inertia::optional()` method to specify that a prop should never be included unless explicitly requested using the `only` option: 143 | 144 | ```php 145 | return Inertia::render('Users/Index', [ 146 | 'users' => Inertia::optional(fn () => User::all()), 147 | ]); 148 | ``` 149 | 150 | On the inverse, you can use the `Inertia::always()` method to specify that a prop should always be included, even if it has not been explicitly required in a partial reload. 151 | 152 | ```php 153 | return Inertia::render('Users/Index', [ 154 | 'users' => Inertia::always(User::all()), 155 | ]); 156 | ``` 157 | 158 | Here's a summary of each approach: 159 | 160 | | Approach | Standard Visits | Partial Reloads | Evaluated | | 161 | |:--------------------|:---------------|:----------------|:----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 162 | | `User::all()` | Always | Optionally | Always | 163 | | `fn () => User::all()` | Always | Optionally | Only when needed | | 164 | | `Inertia::optional(fn () => User::all())` | Never | Optionally | Only when needed | | 165 | | `Inertia::always(fn () => User::all())` | Always | Always | Always | | 166 | 167 | ## Combining with Once Props 168 | 169 | You may chain the `once()` modifier onto an optional prop to ensure the data is resolved only once and remembered by the client across subsequent navigations. 170 | 171 | ```php 172 | return Inertia::render('Users/Index', [ 173 | 'users' => Inertia::optional(fn () => User::all())->once(), 174 | ]); 175 | ``` 176 | 177 | For more information on once props, see the [once props](/v2/data-props/once-props) documentation. 178 | -------------------------------------------------------------------------------- /v2/data-props/flash-data.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Flash Data 3 | --- 4 | 5 | v2.3.3+ 6 | 7 | Sometimes you may wish to send one-time data to your frontend that shouldn't reappear when users navigate through browser history. Unlike regular props, flash data isn't persisted in history state, making it ideal for success messages, newly created IDs, or other temporary values. 8 | 9 | ## Flashing Data 10 | 11 | You may flash data using the `Inertia::flash()` method, passing a key and value or an array of key-value pairs. 12 | 13 | ```php 14 | public function store(Request $request) 15 | { 16 | $user = User::create($request->validated()); 17 | 18 | Inertia::flash('message', 'User created successfully!'); 19 | 20 | // Or flash multiple values at once... 21 | Inertia::flash([ 22 | 'message' => 'User created!', 23 | 'newUserId' => $user->id, 24 | ]); 25 | 26 | return back(); 27 | } 28 | ``` 29 | 30 | Chaining with `back()` is also supported. 31 | 32 | ```php 33 | return Inertia::flash('newUserId', $user->id)->back(); 34 | ``` 35 | 36 | You may also chain `flash()` onto `render()`, or vice versa. 37 | 38 | ```php 39 | return Inertia::render('Projects/Index', [ 40 | 'projects' => $projects, 41 | ])->flash('highlight', $project->id); 42 | 43 | // Or... 44 | 45 | return Inertia::flash('highlight', $project->id) 46 | ->render('Projects/Index', ['projects' => $projects]); 47 | ``` 48 | 49 | Flash data is scoped to the current request. The middleware automatically persists it to the session when redirecting. After the flash data is sent to the client, it is cleared and will not appear in subsequent requests. 50 | 51 | ## Accessing Flash Data 52 | 53 | Flash data is available on `page.flash`. You may also listen for the global `flash` event or use the `onFlash` callback. 54 | 55 | 56 | 57 | ```vue Vue icon="vuejs" 58 | 63 | 64 | 69 | ``` 70 | 71 | ```jsx React icon="react" 72 | import { usePage } from '@inertiajs/react' 73 | 74 | export default function Layout({ children }) { 75 | const { flash } = usePage() 76 | 77 | return ( 78 | <> 79 | {flash.toast &&
{flash.toast.message}
} 80 | {children} 81 | 82 | ) 83 | } 84 | ``` 85 | 86 | ```svelte Svelte icon="s" 87 | 90 | 91 | {#if $page.flash.toast} 92 |
{$page.flash.toast.message}
93 | {/if} 94 | ``` 95 | 96 |
97 | 98 | ## The onFlash Callback 99 | 100 | You may use the `onFlash` callback to handle flash data when making requests. 101 | 102 | 103 | 104 | ```js Vue icon="vuejs" 105 | import { router } from '@inertiajs/vue3' 106 | 107 | router.post('/users', data, { 108 | onFlash: ({ newUserId }) => { 109 | form.userId = newUserId 110 | }, 111 | }) 112 | ``` 113 | 114 | ```js React icon="react" 115 | import { router } from '@inertiajs/react' 116 | 117 | router.post('/users', data, { 118 | onFlash: ({ newUserId }) => { 119 | form.userId = newUserId 120 | }, 121 | }) 122 | ``` 123 | 124 | ```js Svelte icon="s" 125 | import { router } from '@inertiajs/svelte' 126 | 127 | router.post('/users', data, { 128 | onFlash: ({ newUserId }) => { 129 | form.userId = newUserId 130 | }, 131 | }) 132 | ``` 133 | 134 | 135 | 136 | ## Global Flash Event 137 | 138 | You may use the global `flash` event to handle flash data in a central location, such as a layout component. For more information on events, see the [events documentation](/v2/advanced/events). 139 | 140 | 141 | 142 | ```js Vue icon="vuejs" 143 | import { router } from '@inertiajs/vue3' 144 | 145 | router.on('flash', (event) => { 146 | if (event.detail.flash.toast) { 147 | showToast(event.detail.flash.toast) 148 | } 149 | }) 150 | ``` 151 | 152 | ```js React icon="react" 153 | import { router } from '@inertiajs/react' 154 | 155 | router.on('flash', (event) => { 156 | if (event.detail.flash.toast) { 157 | showToast(event.detail.flash.toast) 158 | } 159 | }) 160 | ``` 161 | 162 | ```js Svelte icon="s" 163 | import { router } from '@inertiajs/svelte' 164 | 165 | router.on('flash', (event) => { 166 | if (event.detail.flash.toast) { 167 | showToast(event.detail.flash.toast) 168 | } 169 | }) 170 | ``` 171 | 172 | 173 | 174 | 175 | Event listeners registered inside components should be cleaned up when the component unmounts to prevent them from accumulating and firing multiple times. This is especially important in non-persistent layouts. See [removing event listeners](/v2/advanced/events#removing-listeners) for more information. 176 | 177 | 178 | Native browser events are also supported. 179 | 180 | 181 | 182 | ```js Vue icon="vuejs" 183 | document.addEventListener('inertia:flash', (event) => { 184 | console.log(event.detail.flash) 185 | }) 186 | ``` 187 | 188 | ```js React icon="react" 189 | document.addEventListener('inertia:flash', (event) => { 190 | console.log(event.detail.flash) 191 | }) 192 | ``` 193 | 194 | ```js Svelte icon="s" 195 | document.addEventListener('inertia:flash', (event) => { 196 | console.log(event.detail.flash) 197 | }) 198 | ``` 199 | 200 | 201 | 202 | The `flash` event is not cancelable. During [partial reloads](/v2/data-props/partial-reloads), it only fires if the flash data has changed. 203 | 204 | ## Client-Side Flash 205 | 206 | You may set flash data on the client without a server request using the `router.flash()` method. Values are merged with existing flash data. 207 | 208 | 209 | 210 | ```js Vue icon="vuejs" 211 | import { router } from '@inertiajs/vue3' 212 | 213 | router.flash('foo', 'bar') 214 | router.flash({ foo: 'bar' }) 215 | ``` 216 | 217 | ```js React icon="react" 218 | import { router } from '@inertiajs/react' 219 | 220 | router.flash('foo', 'bar') 221 | router.flash({ foo: 'bar' }) 222 | ``` 223 | 224 | ```js Svelte icon="s" 225 | import { router } from '@inertiajs/svelte' 226 | 227 | router.flash('foo', 'bar') 228 | router.flash({ foo: 'bar' }) 229 | ``` 230 | 231 | 232 | 233 | A callback may also be passed to access the current flash data or replace it entirely. 234 | 235 | 236 | 237 | ```js Vue icon="vuejs" 238 | import { router } from '@inertiajs/vue3' 239 | 240 | router.flash((current) => ({ ...current, bar: 'baz' })) 241 | router.flash(() => ({})) 242 | ``` 243 | 244 | ```js React icon="react" 245 | import { router } from '@inertiajs/react' 246 | 247 | router.flash((current) => ({ ...current, bar: 'baz' })) 248 | router.flash(() => ({})) 249 | ``` 250 | 251 | ```js Svelte icon="s" 252 | import { router } from '@inertiajs/svelte' 253 | 254 | router.flash((current) => ({ ...current, bar: 'baz' })) 255 | router.flash(() => ({})) 256 | ``` 257 | 258 | 259 | 260 | ## TypeScript 261 | 262 | You may configure the flash data type globally using TypeScript's declaration merging. 263 | 264 | ```ts 265 | // global.d.ts 266 | declare module '@inertiajs/core' { 267 | export interface InertiaConfig { 268 | flashDataType: { 269 | toast?: { 270 | type: 'success' | 'error' 271 | message: string 272 | } 273 | } 274 | } 275 | } 276 | ``` 277 | 278 | With this configuration, `page.flash.toast` will be properly typed as `{ type: "success" | "error"; message: string } | undefined`. 279 | -------------------------------------------------------------------------------- /v1/core-concepts/the-protocol.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: The Protocol 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | This page contains a detailed specification of the Inertia protocol. Be sure to read the [how it works](/v1/core-concepts/how-it-works) page first for a high-level overview. 8 | 9 | ## HTML Responses 10 | 11 | The very first request to an Inertia app is just a regular, full-page browser request, with no special Inertia headers or data. For these requests, the server returns a full HTML document. 12 | 13 | This HTML response includes the site assets (CSS, JavaScript) as well as a root `
` in the 14 | page's body. The root `
` serves as a mounting point for the client-side app, and includes a 15 | `data-page` attribute with a JSON encoded page object for the initial 16 | page. Inertia uses this information to boot your client-side framework and display the initial page component. 17 | 18 | ```http 19 | http 20 | REQUEST 21 | GET: https://example.com/events/80 22 | Accept: text/html, application/xhtml+xml 23 | 24 | RESPONSE 25 | HTTP/1.1 200 OK 26 | Content-Type: text/html; charset=utf-8 27 | 28 | 29 | 30 | My app 31 | 32 | 33 | 34 | 35 |
36 | 37 | 38 | ``` 39 | 40 | 41 | While the initial response is HTML, Inertia does not server-side render the JavaScript page components. 42 | 43 | 44 | ## Inertia responses 45 | 46 | Once the Inertia app has been booted, all subsequent requests to the site are made via XHR with a 47 | `X-Inertia` header set to `true`. This header indicates that the request is being made by 48 | Inertia and isn't a standard full-page visit. 49 | 50 | When the server detects the `X-Inertia` header, instead of responding with a full HTML document, it 51 | returns a JSON response with an encoded page object. 52 | 53 | 54 | ```http 55 | REQUEST 56 | GET: https://example.com/events/80 57 | Accept: text/html, application/xhtml+xml 58 | X-Requested-With: XMLHttpRequest 59 | X-Inertia: true 60 | X-Inertia-Version: 6b16b94d7c51cbe5b1fa42aac98241d5 61 | 62 | RESPONSE 63 | HTTP/1.1 200 OK 64 | Content-Type: application/json 65 | Vary: X-Inertia 66 | X-Inertia: true 67 | 68 | { 69 | "component": "Event", 70 | "props": { 71 | "event": { 72 | "id": 80, 73 | "title": "Birthday party", 74 | "start_date": "2019-06-02", 75 | "description": "Come out and celebrate Jonathan's 36th birthday party!" 76 | } 77 | }, 78 | "url": "/events/80", 79 | "version": "c32b8e4965f418ad16eaebba1d4e960f" 80 | } 81 | ``` 82 | 83 | ## The page object 84 | 85 | Inertia shares data between the server and client via a page object. This object includes the necessary 86 | information required to render the page component, update the browser's history state, and track the site's 87 | asset version. The page object includes the following four properties: 88 | 89 | - **component:** The name of the JavaScript page component. 90 | - **props:** The page props (data). 91 | - **url:** The page URL. 92 | - **version:** The current asset version. 93 | 94 | On standard full page visits, the page object is JSON encoded into the `data-page` attribute in the 95 | root `
`. On Inertia visits, the page object is returned as the JSON payload. 96 | 97 | ## Asset versioning 98 | 99 | 100 | One common challenge with single-page apps is refreshing site assets when they've been changed. Inertia makes 101 | this easy by optionally tracking the current version of the site's assets. In the event that an asset changes, 102 | Inertia will automatically make a full-page visit instead of an XHR visit. 103 | 104 | 105 | The Inertia page object includes a `version` identifier. This version 106 | identifier is set server-side and can be a number, string, file hash, or any other value that represents the 107 | current "version" of your site's assets, as long as the value changes when the site's assets have been updated. 108 | 109 | 110 | Whenever an Inertia request is made, Inertia will include the current asset version in the 111 | `X-Inertia-Version` header. When the server receives the request, it compares the asset version 112 | provided in the `X-Inertia-Version` header with the current asset version. This is typically handled 113 | in the middleware layer of your server-side framework. 114 | 115 | 116 | If the asset versions are the same, the request simply continues as expected. However, if the asset versions are 117 | different, the server immediately returns a `409 Conflict` response, and includes the URL in a 118 | `X-Inertia-Location` header. This header is necessary, since server-side redirects may have occurred. 119 | This tells Inertia what the final intended destination URL is. 120 | 121 | 122 | Note, `409 Conflict` responses are only sent for `GET` requests, and not for 123 | `POST/PUT/PATCH/DELETE` requests. That said, they will be sent in the event that a `GET` 124 | redirect occurs after one of these requests. 125 | 126 | 127 | If "flash" session data exists when a `409 Conflict` response occurs, Inertia's server-side framework 128 | adapters will automatically reflash this data. 129 | 130 | ```http 131 | REQUEST 132 | GET: http://example.com/events/80 133 | Accept: text/html, application/xhtml+xml 134 | X-Requested-With: XMLHttpRequest 135 | X-Inertia: true 136 | X-Inertia-Version: 6b16b94d7c51cbe5b1fa42aac98241d5 137 | 138 | RESPONSE 139 | 409: Conflict 140 | X-Inertia-Location: http://example.com/events/80 141 | ``` 142 | 143 | ## Partial reloads 144 | 145 | When making Inertia requests, the partial reload option allows you to request a subset of the props (data) from 146 | the server on subsequent visits to the _same_ page component. This can be a helpful performance 147 | optimization if it's acceptable that some page data becomes stale. 148 | 149 | When a partial reload request is made, Inertia includes two additional headers with the request: 150 | `X-Inertia-Partial-Data` and `X-Inertia-Partial-Component`. 151 | 152 | The `X-Inertia-Partial-Data` header is a comma separated list of the desired props (data) keys that 153 | should be returned. 154 | 155 | The `X-Inertia-Partial-Component` header includes the name of the component that is being partially 156 | reloaded. This is necessary, since partial reloads only work for requests made to the same page component. If 157 | the final destination is different for some reason (eg. the user was logged out and is now on the login page), 158 | then no partial reloading will occur. 159 | 160 | 161 | ```http 162 | REQUEST 163 | GET: http://example.com/events 164 | Accept: text/html, application/xhtml+xml 165 | X-Requested-With: XMLHttpRequest 166 | X-Inertia: true 167 | X-Inertia-Version: 6b16b94d7c51cbe5b1fa42aac98241d5 168 | X-Inertia-Partial-Data: events 169 | X-Inertia-Partial-Component: Events 170 | 171 | RESPONSE 172 | HTTP/1.1 200 OK 173 | Content-Type: application/json 174 | 175 | { 176 | "component": "Events", 177 | "props": { 178 | "auth": {...}, // NOT included 179 | "categories": [...], // NOT included 180 | "events": [...] // included 181 | }, 182 | "url": "/events/80", 183 | "version": "c32b8e4965f418ad16eaebba1d4e960f" 184 | } 185 | ``` 186 | -------------------------------------------------------------------------------- /v1/advanced/remembering-state.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Remembering State 3 | --- 4 | 5 | This is documentation for Inertia.js v1, which is no longer actively maintained. Please refer to the [v2 docs](/v2) for the latest information. 6 | 7 | When navigating browser history, Inertia restores pages using prop data cached in history state. However, Inertia does not restore local page component state since this is beyond its reach. This can lead to outdated pages in your browser history. 8 | 9 | For example, if a user partially completes a form, then navigates away, and then returns back, the form will be reset and their work will be lost. 10 | 11 | To mitigate this issue, you can tell Inertia which local component state to save in the browser history. 12 | 13 | ## Saving Local State 14 | 15 | To save local component state to the history state, use the `remember` feature to tell Inertia which data it should remember. 16 | 17 | 18 | 19 | ```js Vue 2 icon="vuejs" 20 | { 21 | // Option 1: Object... 22 | remember: { 23 | data: ['form'], 24 | }, 25 | 26 | // Option 2: Array... 27 | remember: ['form'], 28 | 29 | // Option 3: String... 30 | remember: 'form', 31 | 32 | data() { 33 | return { 34 | form: { 35 | first_name: null, 36 | last_name: null, 37 | // ... 38 | }, 39 | } 40 | }, 41 | } 42 | ``` 43 | 44 | ```js Vue 3 icon="vuejs" 45 | import { useRemember } from '@inertiajs/vue3' 46 | 47 | const form = useRemember({ 48 | first_name: null, 49 | last_name: null, 50 | }) 51 | ``` 52 | 53 | ```jsx React icon="react" 54 | import { useRemember } from '@inertiajs/react' 55 | 56 | export default function Profile() { 57 | const [formState, setFormState] = useRemember({ 58 | first_name: null, 59 | last_name: null, 60 | // ... 61 | }) 62 | 63 | // ... 64 | } 65 | ``` 66 | 67 | ```js Svelte icon="s" 68 | import { remember } from '@inertiajs/svelte' 69 | 70 | let form = remember({ 71 | first_name: null, 72 | last_name: null, 73 | }) 74 | 75 | // ... 76 | ``` 77 | 78 | 79 | 80 | Now, whenever your local `form` state changes, Inertia will automatically save this data to the history state and will also restore it on history navigation. 81 | 82 | ## Multiple Components 83 | 84 | If your page contains multiple components that use the remember functionality provided by Inertia, you need to provide a unique key for each component so that Inertia knows which data to restore to each component. 85 | 86 | 87 | 88 | ```js Vue 2 icon="vuejs" 89 | { 90 | remember: { 91 | data: ['form'], 92 | key: 'Users/Create', 93 | }, 94 | data() { 95 | return { 96 | form: { 97 | first_name: null, 98 | last_name: null, 99 | }, 100 | } 101 | }, 102 | } 103 | ``` 104 | 105 | ```js Vue 3 icon="vuejs" 106 | import { useRemember } from '@inertiajs/vue3' 107 | 108 | const form = useRemember({ 109 | first_name: null, 110 | last_name: null, 111 | }, 'Users/Create') 112 | ``` 113 | 114 | ```jsx React icon="react" 115 | import { useRemember } from '@inertiajs/react' 116 | 117 | export default function Profile() { 118 | const [formState, setFormState] = useRemember({ 119 | first_name: null, 120 | last_name: null, 121 | }, 'Users/Create') 122 | 123 | } 124 | ``` 125 | 126 | ```js Svelte icon="s" 127 | import { page, remember } from '@inertiajs/svelte' 128 | 129 | let form = remember({ 130 | first_name: null, 131 | last_name: null, 132 | }, 'Users/Create') 133 | ``` 134 | 135 | 136 | 137 | If you have multiple instances of the same component on the page using the remember functionality, be sure to also include a unique key for each component instance, such as a model identifier. 138 | 139 | 140 | 141 | ```js Vue 2 icon="vuejs" 142 | { 143 | remember: { 144 | data: ['form'], 145 | key() { 146 | return `Users/Edit:${this.user.id}` 147 | } 148 | }, 149 | data() { 150 | return { 151 | form: { 152 | first_name: this.user.first_name, 153 | last_name: this.user.last_name, 154 | }, 155 | } 156 | }, 157 | } 158 | ``` 159 | 160 | ```js Vue 3 icon="vuejs" 161 | import { useRemember } from '@inertiajs/vue3' 162 | 163 | const props = defineProps({ user: Object }) 164 | 165 | const form = useRemember({ 166 | first_name: null, 167 | last_name: null, 168 | }, `Users/Edit:${props.user.id}`) 169 | ``` 170 | 171 | ```jsx React icon="react" 172 | import { useRemember } from '@inertiajs/react' 173 | 174 | export default function Profile() { 175 | const [formState, setFormState] = useRemember({ 176 | first_name: props.user.first_name, 177 | last_name: props.user.last_name, 178 | }, `Users/Edit:${this.user.id}`) 179 | } 180 | ``` 181 | 182 | ```js Svelte icon="s" 183 | import { page, remember } from '@inertiajs/svelte' 184 | 185 | let form = remember({ 186 | first_name: $page.props.user.first_name, 187 | last_name: $page.props.user.last_name, 188 | }, `Users/Edit:${$page.props.user.id}`) 189 | ``` 190 | 191 | 192 | 193 | ## Form Helper 194 | 195 | If you're using the [Inertia form helper](/forms#form-helper), you can pass a unique form key as the first argument when instantiating your form. This will cause the form data and errors to automatically be remembered. 196 | 197 | 198 | 199 | ```js Vue 2 icon="vuejs" 200 | import { useForm } from '@inertiajs/vue2' 201 | 202 | form: useForm('CreateUser', data) 203 | form: useForm(`EditUser:${this.user.id}\ 204 | ``` 205 | 206 | ```js Vue 3 icon="vuejs" 207 | import { useForm } from '@inertiajs/vue3' 208 | 209 | const form = useForm('CreateUser', data) 210 | const form = useForm(`EditUser:${props.user.id}\ 211 | ``` 212 | 213 | ```js React icon="react" 214 | import { useForm } from '@inertiajs/react' 215 | 216 | const form = useForm('CreateUser', data) 217 | const form = useForm(`EditUser:${user.id}\ 218 | ``` 219 | 220 | ```js Svelte icon="s" 221 | import { useForm } from '@inertiajs/svelte' 222 | 223 | const form = useForm('CreateUser', data) 224 | const form = useForm(`EditUser:${user.id}\ 225 | ``` 226 | 227 | 228 | 229 | ## Manually Saving State 230 | 231 | The `remember` property in Vue 2, and the `useRemember` hook in Vue 3, React, and Svelte all watch for data changes and automatically save those changes to the history state. Then, Inertia will restore the data on page load. 232 | 233 | However, it's also possible to manage this manually using the underlying `remember()` and `restore()` methods in Inertia. 234 | 235 | 236 | 237 | ```js Vue 2 icon="vuejs" 238 | import { router } from '@inertiajs/vue2' 239 | 240 | // Save local component state to history state... 241 | router.remember(data, 'my-key') 242 | 243 | // Restore local component state from history state... 244 | let data = router.restore('my-key') 245 | ``` 246 | 247 | ```js Vue 3 icon="vuejs" 248 | import { router } from '@inertiajs/vue3' 249 | 250 | // Save local component state to history state... 251 | router.remember(data, 'my-key') 252 | 253 | // Restore local component state from history state... 254 | let data = router.restore('my-key') 255 | ``` 256 | 257 | ```js React icon="react" 258 | import { router } from '@inertiajs/react' 259 | 260 | // Save local component state to history state... 261 | router.remember(data, 'my-key') 262 | 263 | // Restore local component state from history state... 264 | let data = router.restore('my-key') 265 | ``` 266 | 267 | ```js Svelte icon="s" 268 | import { router } from '@inertiajs/svelte' 269 | 270 | // Save local component state to history state... 271 | router.remember(data, 'my-key') 272 | 273 | // Restore local component state from history state... 274 | let data = router.restore('my-key') 275 | ``` 276 | 277 | 278 | -------------------------------------------------------------------------------- /v2/the-basics/title-and-meta.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Title & Meta 3 | --- 4 | 5 | Since Inertia powered JavaScript apps are rendered within the document ``, they are unable to render markup to the document ``, as it's outside of their scope. To help with this, Inertia ships with a `` component which can be used to set the page ``, `<meta>` tags, and other `<head>` elements. 6 | 7 | The `<Head>` component will only replace `<head>` elements that are not in your server-side root template. 8 | 9 | The `<Head>` component is not available in the Svelte adapter, as Svelte already ships with its own `<svelte:head>` component. 10 | 11 | ## Head Component 12 | 13 | To add `<head>` elements to your page, use the `<Head>` component. Within this component, you can include the elements that you wish to add to the document `<head>`. 14 | 15 | <CodeGroup> 16 | 17 | ```vue Vue icon="vuejs" 18 | import { Head } from '@inertiajs/vue3' 19 | 20 | <Head> 21 | <title>Your page title 22 | 23 | 24 | ``` 25 | 26 | ```jsx React icon="react" 27 | import { Head } from '@inertiajs/react' 28 | 29 | 30 | Your page title 31 | 32 | 33 | ``` 34 | 35 | ```svelte Svelte icon="s" 36 | 37 | Your page title 38 | 39 | 40 | ``` 41 | 42 | 43 | 44 | ## Title Shorthand 45 | 46 | If you only need to add a `` to the document `<head>`, you may simply pass the title as a prop to the `<Head>` component. 47 | 48 | <CodeGroup> 49 | 50 | ```vue Vue icon="vuejs" 51 | import { Head } from '@inertiajs/vue3' 52 | 53 | <Head title="Your page title" /> 54 | ``` 55 | 56 | ```jsx React icon="react" 57 | import { Head } from '@inertiajs/react' 58 | 59 | <Head title="Your page title" /> 60 | ``` 61 | 62 | ```js Svelte icon="s" 63 | // Not supported 64 | ``` 65 | 66 | </CodeGroup> 67 | 68 | ## Title Callback 69 | 70 | You can globally modify the page `<title>` using the `title` callback in the `createInertiaApp` setup method. Typically, this method is invoked in your application's main JavaScript file. A common use case for the title callback is automatically adding an app name before or after each page title. 71 | 72 | ```js 73 | createInertiaApp({ 74 | title: title => `${title} - My App`, 75 | // ... 76 | }) 77 | ``` 78 | 79 | After defining the `title` callback, the callback will automatically be invoked when you set a title using the `<Head>` component. 80 | 81 | <CodeGroup> 82 | 83 | ```vue Vue icon="vuejs" 84 | import { Head } from '@inertiajs/vue3' 85 | 86 | <Head title="Home"> 87 | ``` 88 | 89 | ```jsx React icon="react" 90 | import { Head } from '@inertiajs/react' 91 | 92 | <Head title="Home"> 93 | ``` 94 | 95 | ```js Svelte icon="s" 96 | // Not supported 97 | ``` 98 | 99 | </CodeGroup> 100 | 101 | Which, in this example, will result in the following `<title>` tag. 102 | 103 | ```html 104 | <title>Home - My App 105 | ``` 106 | 107 | The `title` callback will also be invoked when you set the title using a `` tag within your `<Head>` component. 108 | 109 | <CodeGroup> 110 | 111 | ```vue Vue icon="vuejs" 112 | import { Head } from '@inertiajs/vue3' 113 | 114 | <Head> 115 | <title>Home 116 | 117 | ``` 118 | 119 | ```jsx React icon="react" 120 | import { Head } from '@inertiajs/react' 121 | 122 | 123 | Home 124 | 125 | ``` 126 | 127 | ```js Svelte icon="s" 128 | // Not supported 129 | ``` 130 | 131 | 132 | 133 | ## Multiple Head Instances 134 | 135 | It's possible to have multiple instances of the `` component throughout your application. For example, your layout can set some default `` elements, and then your individual pages can override those defaults. 136 | 137 | 138 | 139 | ```vue Vue icon="vuejs" 140 | // Layout.vue 141 | 142 | import { Head } from '@inertiajs/vue3' 143 | 144 | 145 | My app 146 | 147 | 148 | 149 | 150 | // About.vue 151 | 152 | import { Head } from '@inertiajs/vue3' 153 | 154 | 155 | About - My app 156 | 157 | 158 | ``` 159 | 160 | ```jsx React icon="react" 161 | // Layout.js 162 | 163 | import { Head } from '@inertiajs/react' 164 | 165 | 166 | My app 167 | 168 | 169 | 170 | 171 | // About.js 172 | 173 | import { Head } from '@inertiajs/react' 174 | 175 | 176 | About - My app 177 | 178 | 179 | ``` 180 | 181 | ```js Svelte icon="s" 182 | // Not supported 183 | ``` 184 | 185 | 186 | 187 | Inertia will only ever render one `` tag; however, all other tags will be stacked since it's valid to have multiple instances of them. To avoid duplicate tags in your `<head>`, you can use the `head-key` property, which will make sure the tag is only rendered once. This is illustrated in the example above for the `<meta name="description">` tag. 188 | 189 | The code example above will render the following HTML. 190 | 191 | ```html 192 | <head> 193 | <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> 194 | <title>About - My app 195 | 196 | 197 | ``` 198 | 199 | ## Head Extension 200 | 201 | When building a real application, it can sometimes be helpful to create a custom head component that extends Inertia's `` component. This gives you a place to set app-wide defaults, such as appending the app name to the page title. 202 | 203 | 204 | 205 | ```vue Vue icon="vuejs" 206 | 207 | 208 | 213 | 214 | 219 | ``` 220 | 221 | ```jsx React icon="react" 222 | // AppHead.js 223 | 224 | import { Head } from '@inertiajs/react' 225 | 226 | const Site = ({ title, children }) => { 227 | return ( 228 | 229 | {title ? `${title} - My App` : 'My App'} 230 | {children} 231 | 232 | ) 233 | } 234 | 235 | export default Site 236 | ``` 237 | 238 | ```js Svelte icon="s" 239 | // Not supported 240 | ``` 241 | 242 | 243 | 244 | Once you have created the custom component, you can just start using it in your pages. 245 | 246 | 247 | 248 | ```vue Vue icon="vuejs" 249 | import AppHead from './AppHead' 250 | 251 | 252 | ``` 253 | 254 | ```jsx React icon="react" 255 | import AppHead from './AppHead' 256 | 257 | 258 | ``` 259 | 260 | ```js Svelte icon="s" 261 | // Not supported 262 | ``` 263 | 264 | 265 | 266 | ## Inertia Attribute on Elements 267 | 268 | Inertia has historically used the `inertia` attribute to track and manage elements in the document ``. However, you can now opt-in to using the more standards-compliant `data-inertia` attribute instead. According to the HTML specification, custom attributes should be prefixed with `data-` to avoid conflicts with future HTML standards. 269 | 270 | To enable this, configure the `future.useDataInertiaHeadAttribute` option in your [application defaults](/v2/installation/client-side-setup#configuring-defaults). 271 | 272 | ```js 273 | createInertiaApp({ 274 | // resolve, setup, etc. 275 | defaults: { 276 | future: { 277 | useDataInertiaHeadAttribute: true, 278 | }, 279 | }, 280 | }) 281 | ``` 282 | --------------------------------------------------------------------------------