diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 0000000..2dc5e7c
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,55 @@
+name: Deploy Docs
+
+on:
+ push:
+ branches: [main]
+
+ workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+ group: pages
+ cancel-in-progress: false
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: npm
+ - name: Setup Pages
+ uses: actions/configure-pages@v4
+ - name: Install dependencies
+ run: npm ci
+ - name: Build with VitePress
+ run: npm run docs:build
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: docs/.vitepress/dist
+
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ needs: build
+ runs-on: ubuntu-latest
+ name: Deploy
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/docs/.gitignore b/docs/.gitignore
new file mode 100644
index 0000000..57a09c3
--- /dev/null
+++ b/docs/.gitignore
@@ -0,0 +1,3 @@
+node_modules
+.vitepress/dist
+.vitepress/cache
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
new file mode 100644
index 0000000..0930c19
--- /dev/null
+++ b/docs/.vitepress/config.mts
@@ -0,0 +1,107 @@
+import { defineConfig } from "vitepress";
+
+const title = "Inertia Django";
+const description = "Build single page apps, without building an API";
+const site = "https://inertia-django.dev";
+const image = `${site}/og_image.png`;
+
+// https://vitepress.dev/reference/site-config
+export default defineConfig({
+ title: title,
+ description: description,
+
+ cleanUrls: true,
+
+ head: [
+ ["link", { rel: "icon", href: "/favicon.ico", sizes: "32x32" }],
+ ["link", { rel: "icon", href: "/icon.svg", type: "image/svg+xml" }],
+
+ ["meta", { name: "twitter:card", content: "summary_large_image" }],
+ ["meta", { name: "twitter:site", content: site }],
+ ["meta", { name: "twitter:description", value: description }],
+ ["meta", { name: "twitter:image", content: image }],
+
+ ["meta", { property: "og:type", content: "website" }],
+ ["meta", { property: "og:locale", content: "en_US" }],
+ ["meta", { property: "og:site", content: site }],
+ ["meta", { property: "og:site_name", content: title }],
+ ["meta", { property: "og:image", content: image }],
+ ["meta", { property: "og:description", content: description }],
+ ],
+
+ themeConfig: {
+ // https://vitepress.dev/reference/default-theme-config
+ nav: [
+ { text: "Home", link: "/" },
+ { text: "Guide", link: "/guide" },
+ {
+ text: "Links",
+ items: [
+ {
+ text: "Official Inertia.js docs",
+ link: "https://inertiajs.com",
+ },
+ {
+ text: "inertia-django on PyPI",
+ link: "https://pypi.org/project/inertia-django",
+ },
+ ],
+ },
+ ],
+
+ logo: "/logo.svg",
+
+ sidebar: {
+ "/guide/": [
+ {
+ items: [{ text: "Introduction", link: "/guide" }],
+ },
+ {
+ text: "Installation",
+ items: [
+ {
+ text: "Server-side",
+ link: "/guide/server-side-setup",
+ },
+ {
+ text: "Client-side",
+ link: "/guide/client-side-setup",
+ },
+ ],
+ },
+ {
+ text: "Core concepts",
+ items: [
+ { text: "Who is it for", link: "/guide/who-is-it-for" },
+ { text: "How it works", link: "/guide/how-it-works" },
+ { text: "The protocol", link: "/guide/the-protocol" },
+ ],
+ },
+ {
+ text: "The basics",
+ items: [{ text: "Links", link: "/guide/links" }],
+ },
+ {
+ text: "Advanced",
+ items: [
+ {
+ text: "Scroll management",
+ link: "/guide/scroll-management",
+ },
+ {
+ text: "Code splitting",
+ link: "/guide/code-splitting",
+ },
+ ],
+ },
+ ],
+ },
+
+ socialLinks: [
+ {
+ icon: "github",
+ link: "https://github.com/inertiajs/inertia-django",
+ },
+ ],
+ },
+});
diff --git a/docs/guide/client-side-setup.md b/docs/guide/client-side-setup.md
new file mode 100644
index 0000000..0159d35
--- /dev/null
+++ b/docs/guide/client-side-setup.md
@@ -0,0 +1,328 @@
+# Client-side setup
+
+::: tip
+If you used the [template based setup](/guide/server-side-setup.md#template-based-setup), you can skip client-side setup.
+It is already configured as part of the template based setup.
+:::
+
+Once you have [server-side configured](/guide/server-side-setup.md), you then need to setup your client-side framework. Inertia currently provides support for React, Vue, and Svelte.
+
+## Install dependencies
+
+First, install the Inertia client-side adapter corresponding to your framework of choice.
+
+::: code-group
+
+```shell [Vue]
+npm install @inertiajs/vue3 vue @vitejs/plugin-vue
+```
+
+```shell [React]
+npm install @inertiajs/react react react-dom @vitejs/plugin-react
+```
+
+```shell [Svelte 4, Svelte 5]
+npm install @inertiajs/svelte svelte @sveltejs/vite-plugin-svelte
+```
+
+:::
+
+## Configure Vite
+
+Create a `vite.config.js` file in your root directory and configure it for use with your frontend of choice and `django-vite`.
+
+::: code-group
+
+```js [Vue]
+// vite.config.js
+import { join, resolve } from "node:path";
+import vue from "@vitejs/plugin-vue";
+import { defineConfig, loadEnv } from "vite";
+
+
+export default defineConfig((mode) => {
+ const env = loadEnv(mode, process.cwd(), "");
+
+ const INPUT_DIR = "./frontend";
+ const OUTPUT_DIR = "./frontend/dist";
+
+ return {
+ plugins: [vue()],
+ resolve: {
+ alias: {
+ "@": resolve(INPUT_DIR),
+ vue: "vue/dist/vue.esm-bundler.js",
+ },
+ },
+ root: resolve(INPUT_DIR),
+ base: "/static/",
+ server: {
+ host: "0.0.0.0",
+ port: env.DJANGO_VITE_DEV_SERVER_PORT || 5173,
+ watch: {
+ usePolling: true,
+ },
+ },
+ build: {
+ manifest: "manifest.json",
+ emptyOutDir: true,
+ outDir: resolve(OUTPUT_DIR),
+ rollupOptions: {
+ input: {
+ main: join(INPUT_DIR, "/js/main.js"),
+ css: join(INPUT_DIR, "/css/main.css"),
+ },
+ },
+ },
+ };
+});
+
+```
+
+```js [React]
+// vite.config.js
+import { join, resolve } from "node:path";
+import react from '@vitejs/plugin-react';
+import { defineConfig, loadEnv } from "vite";
+
+
+export default defineConfig((mode) => {
+ const env = loadEnv(mode, process.cwd(), "");
+
+ const INPUT_DIR = "./frontend";
+ const OUTPUT_DIR = "./frontend/dist";
+
+ return {
+ plugins: [react()],
+ resolve: {
+ alias: {
+ "@": resolve(INPUT_DIR),
+ },
+ },
+ root: resolve(INPUT_DIR),
+ base: "/static/",
+ server: {
+ host: "0.0.0.0",
+ port: env.DJANGO_VITE_DEV_SERVER_PORT || 5173,
+ watch: {
+ usePolling: true,
+ },
+ },
+ build: {
+ manifest: "manifest.json",
+ emptyOutDir: true,
+ outDir: resolve(OUTPUT_DIR),
+ rollupOptions: {
+ input: {
+ main: join(INPUT_DIR, "/js/main.js"),
+ css: join(INPUT_DIR, "/css/main.css"),
+ },
+ },
+ },
+ };
+});
+```
+
+```js [Svelte]
+// vite.config.js
+import { join, resolve } from "node:path";
+import { svelte } from '@sveltejs/vite-plugin-svelte';
+import { defineConfig, loadEnv } from "vite";
+
+
+export default defineConfig((mode) => {
+ const env = loadEnv(mode, process.cwd(), "");
+
+ const INPUT_DIR = "./frontend";
+ const OUTPUT_DIR = "./frontend/dist";
+
+ return {
+ plugins: [svelte()],
+ resolve: {
+ alias: {
+ "@": resolve(INPUT_DIR),
+ },
+ },
+ root: resolve(INPUT_DIR),
+ base: "/static/",
+ server: {
+ host: "0.0.0.0",
+ port: env.DJANGO_VITE_DEV_SERVER_PORT || 5173,
+ watch: {
+ usePolling: true,
+ },
+ },
+ build: {
+ manifest: "manifest.json",
+ emptyOutDir: true,
+ outDir: resolve(OUTPUT_DIR),
+ rollupOptions: {
+ input: {
+ main: join(INPUT_DIR, "/js/main.js"),
+ css: join(INPUT_DIR, "/css/main.css"),
+ },
+ },
+ },
+ };
+});
+```
+:::
+
+## Initialize the Inertia app
+
+Create a `frontend` directory in your root directory and add a `js` directory inside it.
+Inside the `js` directory, create a `main.js` file.
+
+Next, update your main JavaScript file (`main.js`) to boot your Inertia app.
+To accomplish this, we'll initialize the client-side framework with the base Inertia component.
+
+We will also configure CSRF to work properly with Django.
+
+::: code-group
+
+```js [Vue]
+// frontend/js/main.js
+import { createApp, h } from "vue";
+import { createInertiaApp } from "@inertiajs/vue3";
+import axios from "axios";
+
+document.addEventListener("DOMContentLoaded", () => {
+ axios.defaults.xsrfCookieName = "csrftoken";
+ axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
+
+ createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.vue", { eager: true });
+ return pages[`../pages/${name}.vue`];
+ },
+ setup({ el, App, props, plugin }) {
+ createApp({ render: () => h(App, props) })
+ .use(plugin)
+ .mount(el);
+ },
+ });
+});
+```
+
+```js [React]
+// frontend/js/main.js
+import { createInertiaApp } from "@inertiajs/react";
+import { createElement } from "react";
+import { createRoot } from "react-dom/client";
+
+import axios from "axios";
+
+document.addEventListener("DOMContentLoaded", () => {
+ axios.defaults.xsrfCookieName = "csrftoken";
+ axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
+
+ createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.jsx", { eager: true });
+ return pages[`../pages/${name}.jsx`];
+ },
+ setup({ el, App, props }) {
+ const root = createRoot(el);
+ root.render(createElement(App, props));
+ },
+ });
+});
+```
+
+```js [Svelte 4]
+// frontend/js/main.js
+import { createInertiaApp } from "@inertiajs/svelte";
+import axios from "axios";
+
+document.addEventListener("DOMContentLoaded", () => {
+ axios.defaults.xsrfCookieName = "csrftoken";
+ axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
+
+ createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.svelte", { eager: true });
+ return pages[`../pages/${name}.svelte`];
+ },
+ setup({ el, App, props }) {
+ new App({ target: el, props });
+ },
+ });
+});
+```
+
+```js [Svelte 5]
+// frontend/js/main.js
+import { createInertiaApp } from "@inertiajs/svelte";
+import { mount } from "svelte";
+
+createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("./Pages/**/*.svelte", { eager: true });
+ return pages[`./Pages/${name}.svelte`];
+ },
+ setup({ el, App, props }) {
+ mount(App, { target: el, props });
+ },
+});
+```
+
+:::
+
+The `setup` callback receives everything necessary to initialize the client-side framework, including the root Inertia `App` component.
+
+# Resolving components
+
+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.
+
+::: code-group
+
+```js [Vue]
+// frontend/js/main.js
+createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.vue", { eager: true });
+ return pages[`../pages/${name}.vue`];
+ },
+ // ...
+});
+```
+
+```js [React]
+// frontend/js/main.js
+createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.jsx", { eager: true });
+ return pages[`../pages/${name}.jsx`];
+ },
+ //...
+});
+```
+
+```js [Svelte 4, Svelte 5]
+// frontend/js/main.js
+createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.svelte", { eager: true });
+ return pages[`../pages/${name}.svelte`];
+ },
+ //...
+});
+```
+
+:::
+
+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](/guide/code-splitting.md) documentation.
+
+## Defining a root element
+
+By default, Inertia assumes that your application's root template has a root element with an `id` of `app`.
+This is already configured if you use the `{% block inertia %} {% endblock %}` template tag from `inertia-django` in your base html template.
+
+If your application's root element has a different `id`, you can provide it using the `id` property.
+
+```js
+createInertiaApp({
+ id: "my-app",
+ // ...
+});
+```
diff --git a/docs/guide/code-splitting.md b/docs/guide/code-splitting.md
new file mode 100644
index 0000000..1a61a17
--- /dev/null
+++ b/docs/guide/code-splitting.md
@@ -0,0 +1,54 @@
+# Code splitting
+
+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.
+
+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.
+
+To enable code splitting you'll need to tweak the resolve callback in your `createInertiaApp()` configuration, and how you do this is different depending on which bundler you're using.
+
+## Using Vite
+
+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.
+
+::: code-group
+
+```js [Vue]
+// frontend/entrypoints/inertia.js
+createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.vue", { eager: true }); // [!code --]
+ return pages[`../pages/${name}.vue`]; // [!code --]
+ const pages = import.meta.glob("../pages/**/*.vue"); // [!code ++]
+ return pages[`../pages/${name}.vue`](); // [!code ++]
+ },
+ //...
+});
+```
+
+```js [React]
+// frontend/entrypoints/inertia.js
+createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.jsx", { eager: true }); // [!code --]
+ return pages[`../pages/${name}.jsx`]; // [!code --]
+ const pages = import.meta.glob("../pages/**/*.jsx"); // [!code ++]
+ return pages[`../pages/${name}.jsx`](); // [!code ++]
+ },
+ //...
+});
+```
+
+```js [Svelte 4, Svelte 5]
+// frontend/entrypoints/inertia.js
+createInertiaApp({
+ resolve: (name) => {
+ const pages = import.meta.glob("../pages/**/*.svelte", { eager: true }); // [!code --]
+ return pages[`../pages/${name}.svelte`]; // [!code --]
+ const pages = import.meta.glob("../pages/**/*.svelte"); // [!code ++]
+ return pages[`../pages/${name}.svelte`](); // [!code ++]
+ },
+ //...
+});
+```
+
+:::
diff --git a/docs/guide/how-it-works.md b/docs/guide/how-it-works.md
new file mode 100644
index 0000000..b2f2c72
--- /dev/null
+++ b/docs/guide/how-it-works.md
@@ -0,0 +1,15 @@
+# How it works
+
+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.
+
+However, Inertia replaces your application's view layer. Instead of using server-side rendering via Django 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 Django.
+
+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.
+
+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()`.
+
+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.
+
+**The end result is a silky smooth single-page experience. :tada:**
+
+To learn more about the nitty-gritty, technical details of how Inertia works under the hood, check out [the protocol page](/guide/the-protocol.md).
diff --git a/docs/guide/index.md b/docs/guide/index.md
new file mode 100644
index 0000000..ad58398
--- /dev/null
+++ b/docs/guide/index.md
@@ -0,0 +1,23 @@
+# Introduction
+
+Welcome to the documentation for [inertia-django](https://github.com/inertiajs/inertia-django) adapter for [Django](https://www.djangoproject.com/) and [Inertia.js](https://inertiajs.com/).
+
+## Why adapter-specific documentation?
+
+The [official documentation for Inertia.js](https://inertiajs.com) is great, but it's not Django-specific. This documentation aims to fill in the gaps and provide Django-specific examples and explanations.
+
+## JavaScript apps the monolith way
+
+Inertia is a new approach to building classic server-driven web apps. We call it the modern monolith.
+
+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.
+
+**Inertia has no client-side routing, nor does it require an API.** Simply write Django like you've always done!
+
+### Not a framework
+
+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).
+
+### Next steps
+
+Want to learn a bit more before diving in? Check out the [who is it for](/guide/who-is-it-for.md) and [how it works](/guide/how-it-works.md) pages. Or, if you're ready to get started, jump right into the [installation instructions](/guide/server-side-setup.md).
diff --git a/docs/guide/links.md b/docs/guide/links.md
new file mode 100644
index 0000000..3c84086
--- /dev/null
+++ b/docs/guide/links.md
@@ -0,0 +1,491 @@
+# Links
+
+To create links to other pages within an Inertia app, you will typically use the Inertia `` component. This component is a light wrapper around a standard anchor `` link that intercepts click events and prevents full page reloads. This is [how Inertia provides a single-page app experience](/guide/how-it-works.md) once your application has been loaded.
+
+## Creating links
+
+To create an Inertia link, use the Inertia `` component. Any attributes you provide to this component will be proxied to the underlying HTML tag.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+ Home
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => Home;
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+Home
+
+Home
+```
+
+:::
+
+::: tip
+
+For Svelte, the `use:inertia` action can be applied to any HTML element.
+
+:::
+
+By default, Inertia renders links as anchor `` elements. However, you can change the tag using the `as` prop.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+ Logout
+
+
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+
+ Logout
+
+);
+
+// Renders as...
+//
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+Logout
+
+
+
+```
+
+:::
+
+::: info
+
+Creating `POST/PUT/PATCH/DELETE` anchor `` links is discouraged as it causes "Open Link in New Tab / Window" accessibility issues. The component automatically renders a `
+```
+
+:::
+
+## Data
+
+When making `POST` or `PUT` requests, you may wish to add additional data to the request. You can accomplish this using the `data` prop. The provided data can be an `object` or `FormData` instance.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+
+ Save
+
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+
+ Save
+
+);
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+
+ Save
+
+
+Save
+```
+
+:::
+
+## Custom headers
+
+The `headers` prop allows you to add custom headers to an Inertia link. However, the headers Inertia uses internally to communicate its state to the server take priority and therefore cannot be overwritten.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+ Save
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+
+ Save
+
+);
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+Save
+
+Save
+```
+
+:::
+
+## Browser history
+
+The `replace` prop allows you to specify the browser's history behavior. By default, page visits push (new) state (`window.history.pushState`) into the history; however, it's also possible to replace state (`window.history.replaceState`) by setting the `replace` prop to `true`. This will cause the visit to replace the current history state instead of adding a new history state to the stack.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+ Home
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+
+ Home
+
+);
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+Home
+
+Home
+```
+
+:::
+
+## State preservation
+
+You can preserve a page component's local state using the `preserveState` prop. This will prevent a page component from fully re-rendering. The `preserveState` prop is especially helpful on pages that contain forms, since you can avoid manually repopulating input fields and can also maintain a focused input.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+
+
+ Search
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+ <>
+
+
+
+ Search
+
+ >
+);
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+
+
+
+ Search
+
+
+Search
+```
+
+:::
+
+## Scroll preservation
+
+You can use the `preserveScroll` prop to prevent Inertia from automatically resetting the scroll position when making a page visit.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+ Home
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+
+ Home
+
+);
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+Home
+
+Home
+```
+
+:::
+
+For more information on managing scroll position, please consult the documentation on [scroll management](/guide/scroll-management).
+
+## Partial reloads
+
+The `only` prop allows you to specify that only a subset of a page's props (data) should be retrieved from the server on subsequent visits to that page.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+ Show active
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+
+ Show active
+
+);
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+Show active
+
+Show active
+```
+
+:::
+
+For more information on this topic, please consult the complete documentation on [partial reloads](/guide/partial-reloads.md).
+
+## Active states
+
+It's often desirable to set an active state for navigation links based on the current page. This can be accomplished when using Inertia by inspecting the `page` object and doing string comparisons against the `page.url` and `page.component` properties.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+
+ Users
+
+
+
+ Users
+
+
+
+
+ Users
+
+
+
+
+ Users
+
+
+```
+
+```jsx [React]
+import { usePage } from "@inertiajs/react";
+
+export default () => {
+ const { url, component } = usePage();
+
+ return (
+ <>
+ // URL exact match...
+
+ Users
+
+ // Component exact match...
+
+ Users
+
+ // URL starts with (/users, /users/create, /users/1, etc.)...
+
+ Users
+
+ // Component starts with (Users/Index, Users/Create, Users/Show,
+ etc.)...
+
+ Users
+
+ >
+ );
+};
+```
+
+```svelte [Svelte 4 , Svelte 5]
+
+
+
+
+ Users
+
+
+
+ Users
+
+
+
+
+ Users
+
+
+
+
+ Users
+
+
+```
+
+:::
+
+You can perform exact match comparisons (`===`), `startsWith()` comparisons (useful for matching a subset of pages), or even more complex comparisons using regular expressions.
+
+Using this approach, you're not limited to just setting class names. You can use this technique to conditionally render any markup on active state, such as different link text or even an SVG icon that represents the link is active.
+
+## Data loading attribute
+
+While a link is making an active request, a `data-loading` attribute is added to the link element. This allows you to style the link while it's in a loading state. The attribute is removed once the request is complete.
diff --git a/docs/guide/partial-reloads.md b/docs/guide/partial-reloads.md
new file mode 100644
index 0000000..a52cfd6
--- /dev/null
+++ b/docs/guide/partial-reloads.md
@@ -0,0 +1,3 @@
+# Partial reloads
+
+Coming soon...
diff --git a/docs/guide/scroll-management.md b/docs/guide/scroll-management.md
new file mode 100644
index 0000000..d36679f
--- /dev/null
+++ b/docs/guide/scroll-management.md
@@ -0,0 +1,135 @@
+# Scroll management
+
+## Scroll resetting
+
+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.
+
+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.
+
+## Scroll preservation
+
+Sometimes it's desirable to prevent the default scroll resetting when making visits. You can disable this behaviour by setting the `preserveScroll` option to `false`.
+
+::: code-group
+
+```js [Vue]
+import { router } from "@inertiajs/vue3";
+
+router.visit(url, { preserveScroll: false });
+```
+
+```js [React]
+import { router } from "@inertiajs/react";
+
+router.visit(url, { preserveScroll: false });
+```
+
+```js [Svelte 4, Svelte 5]
+import { router } from "@inertiajs/svelte";
+
+router.visit(url, { preserveScroll: false });
+```
+
+:::
+
+If you'd like to only preserve the scroll position if the response includes validation errors, set the `preserveScroll` option to `"errors"`.
+
+::: code-group
+
+```js [Vue]
+import { router } from "@inertiajs/vue3";
+
+router.visit(url, { preserveScroll: "errors" });
+```
+
+```js [React]
+import { router } from "@inertiajs/react";
+
+router.visit(url, { preserveScroll: "errors" });
+```
+
+```js [Svelte 4, Svelte 5]
+import { router } from "@inertiajs/svelte";
+
+router.visit(url, { preserveScroll: "errors" });
+```
+
+:::
+
+You can also lazily evaluate the `preserveScroll` option based on the response by providing a callback.
+
+::: code-group
+
+```js [Vue]
+import { router } from "@inertiajs/vue3";
+
+router.post("/users", data, {
+ preserveScroll: (page) => page.props.someProp === "value",
+});
+```
+
+```js [React]
+import { router } from "@inertiajs/react";
+
+router.post("/users", data, {
+ preserveScroll: (page) => page.props.someProp === "value",
+});
+```
+
+```js [Svelte 4, Svelte 5]
+import { router } from "@inertiajs/svelte";
+
+router.post("/users", data, {
+ preserveScroll: (page) => page.props.someProp === "value",
+});
+```
+
+:::
+
+When using an [Inertia link](/guide/links), you can preserve the scroll position using the `preserveScroll` prop.
+
+::: code-group
+
+```vue [Vue]
+
+
+
+ Home
+
+```
+
+```jsx [React]
+import { Link } from "@inertiajs/react";
+
+export default () => (
+
+ Home
+
+);
+```
+
+```svelte [Svelte 4, Svelte 5]
+
+
+Home
+
+Home
+```
+
+:::
+
+## Scroll regions
+
+If your app doesn't use document body scrolling, but instead has scrollable elements (using the `overflow` CSS property), scroll resetting will not work.
+
+In these situations, you must tell Inertia which scrollable elements to manage by adding the `scroll-region` attribute to the element.
+
+```html
+
+
+
+```
diff --git a/docs/guide/server-side-setup.md b/docs/guide/server-side-setup.md
new file mode 100644
index 0000000..015acf3
--- /dev/null
+++ b/docs/guide/server-side-setup.md
@@ -0,0 +1,158 @@
+# Server-side setup
+
+To get started with Inertia in Django, the first step is to configure Django to use the `inertia-django` server-side adapter.
+
+::: tip
+For new projects, we recommend following the [template based setup](#template-based-setup) guide.
+:::
+
+## Template-based setup
+
+If you're starting a new project, we recommend configuring Django and Inertia using a `django-vite-inertia` template.
+
+This will require [uv](https://docs.astral.sh/uv/) or [pipx](https://pipx.pypa.io/stable/) to be installed.
+
+First, let's create a new directory for our project.
+
+```shell
+mkdir myproject
+```
+Next, we'll use the template based installer.
+
+::: code-group
+
+```shell [uv]
+uvx copier copy gh:sarthakjariwala/django-vite-inertia myproject
+```
+
+```shell [pipx]
+pipx run copier copy gh:sarthakjariwala/django-vite-inertia myproject
+
+```
+
+:::
+
+This command will:
+
+- Create a new Django project in your `myproject` directory
+- Ask you to choose your preferred frontend framework:
+ - React
+ - Vue
+ - Svelte
+- Ask you if you want to use Tailwind CSS
+- Ask you the database you want to use
+ - SQLite
+ - PostgreSQL
+- Ask you if you want to use Docker in development
+- Ask you if you want to use Docker in production
+- Set up Vite integration via `django-vite`
+- Set up Django to work with Inertia
+- Initialize the Inertia app
+- Configure CSRF to work properly with Django
+
+Example output:
+```
+? Your project name: todo
+? Which database do you want to use? postgresql
+? Which frontend do you want to use? vue
+? Do you want to use Tailwind CSS? Yes
+? Do you want to use Docker in development? Yes
+? Do you want to use Docker in production? Yes
+```
+
+You're all set! You can now start your development server.
+
+## Manual setup
+
+::: tip
+Skip this section if you used the [template based setup](#template-based-setup) method.
+:::
+
+### Install dependencies
+
+Install the `inertia-django` server-side adapter and related dependencies.
+
+::: code-group
+
+```shell [uv]
+uv add inertia-django django-vite
+```
+
+```shell [pip]
+python -m pip install inertia-django django-vite
+```
+:::
+
+### Update installed apps
+
+Add `django_vite` and `inertia` to your `INSTALLED_APPS` in `settings.py`.
+
+```python
+INSTALLED_APPS = [
+ # ...
+ "django_vite",
+ "inertia",
+]
+```
+
+### Update middleware
+
+Next, add `inertia.middleware.InertiaMiddleware` to your `MIDDLEWARE` in `settings.py`.
+
+```python
+MIDDLEWARE = [
+ # ...
+ "inertia.middleware.InertiaMiddleware",
+]
+```
+
+### Configure settings
+
+Configure `django-vite` and `inertia-django` specific settings in `settings.py`.
+
+```python
+# django-vite settings
+# If using HMR (hot module replacement)
+DJANGO_VITE = {
+ "default": {
+ "dev_mode": DEBUG,
+ "dev_server_host": env.str("DJANGO_VITE_DEV_SERVER_HOST", default="localhost"),
+ "dev_server_port": env.int("DJANGO_VITE_DEV_SERVER_PORT", default=5173),
+ }
+}
+
+# Where ViteJS assets are built.
+DJANGO_VITE_ASSETS_PATH = BASE_DIR / "src" / "dist"
+
+# Include DJANGO_VITE_ASSETS_PATH into STATICFILES_DIRS to be copied inside
+# when run command python manage.py collectstatic
+STATICFILES_DIRS = [DJANGO_VITE_ASSETS_PATH]
+```
+
+> For a complete list of available `vite` related settings, see `django-vite` [docs](https://github.com/MrBin99/django-vite?tab=readme-ov-file#django-vite).
+
+```python
+# inertia-django settings
+INERTIA_LAYOUT = "base.html" # update with your base template name
+```
+
+> For a complete list of available `inertia-django` settings, see [readme](https://github.com/inertiajs/inertia-django?tab=readme-ov-file#settings).
+
+### Update base template
+
+In your base html template, add the following:
+
+```html
+{% load django_vite %}
+
+...
+
+ ...
+ {% vite_hmr_client %}
+ {% vite_asset 'js/main.js' %}
+
+
+
+ {% block inertia %}{% endblock %}
+
+```
diff --git a/docs/guide/the-protocol.md b/docs/guide/the-protocol.md
new file mode 100644
index 0000000..a2bc006
--- /dev/null
+++ b/docs/guide/the-protocol.md
@@ -0,0 +1,151 @@
+# The protocol
+
+This page contains a detailed specification of the Inertia protocol. Be sure to read the [how it works](/guide/how-it-works.md) page first for a high-level overview.
+
+## HTML responses
+
+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.
+
+This HTML response includes the site assets (CSS, JavaScript) as well as a root `
` in the page's body. The root `
` serves as a mounting point for the client-side app, and includes a `data-page` attribute with a JSON encoded [page object] for the initial page. Inertia uses this information to boot your client-side framework and display the initial page component.
+
+```http
+REQUEST
+GET: http://example.com/events/80
+Accept: text/html, application/xhtml+xml
+
+
+RESPONSE
+HTTP/1.1 200 OK
+Content-Type: text/html; charset=utf-8
+
+
+
+ My app
+
+
+
+
+
+
+
+
+
+```
+
+> [!NOTE]
+> While the initial response is HTML, Inertia does not server-side render the JavaScript page components.
+
+## Inertia responses
+
+Once the Inertia app has been booted, all subsequent requests to the site are made via XHR with a `X-Inertia` header set to `true`. This header indicates that the request is being made by Inertia and isn't a standard full-page visit.
+
+When the server detects the `X-Inertia` header, instead of responding with a full HTML document, it returns a JSON response with an encoded [page object].
+
+```http
+REQUEST
+GET: http://example.com/events/80
+Accept: text/html, application/xhtml+xml
+X-Requested-With: XMLHttpRequest
+X-Inertia: true
+X-Inertia-Version: 6b16b94d7c51cbe5b1fa42aac98241d5
+
+RESPONSE
+HTTP/1.1 200 OK
+Content-Type: application/json
+Vary: X-Inertia
+X-Inertia: true
+
+{
+ "component": "Event",
+ "props": {
+ "event": {
+ "id": 80,
+ "title": "Birthday party",
+ "start_date": "2019-06-02",
+ "description": "Come out and celebrate Jonathan's 36th birthday party!"
+ }
+ },
+ "url": "/events/80",
+ "version": "c32b8e4965f418ad16eaebba1d4e960f",
+ "encryptHistory": true,
+ "clearHistory": false
+}
+```
+
+## The page object
+
+Inertia shares data between the server and client via a page object. This object includes the necessary information required to render the page component, update the browser's history state, and track the site's asset version. The page object includes the following four properties:
+
+1. `component`: The name of the JavaScript page component.
+2. `props`: The page props (data).
+3. `url`: The page URL.
+4. `version`: The current asset version.
+5. `encryptHistory`: Whether or not to encrypt the current page's history state.
+6. `clearHistory`: Whether or not to clear any encrypted history state.
+
+On standard full page visits, the page object is JSON encoded into the `data-page` attribute in the root `