A lightweight Fastify plugin for Inertia.js that lets you build modern single-page applications with server-side rendering. It allows seamless integration of React, Vue, or Svelte components while preserving the simplicity of classic apps.
The fastest way to get started is using our official templates:
# For React
npx degit mahendra7041/fastify-inertia/examples/react my-inertia-app
# For Vue
npx degit mahendra7041/fastify-inertia/examples/vue my-inertia-app
# For Svelte
npx degit mahendra7041/fastify-inertia/examples/svelte my-inertia-app
cd my-inertia-app
npm install
npm run dev
First, create a new project using Vite with your preferred framework:
# For React (used in this guide)
npm create vite@latest my-inertia-app -- --template react
# For Vue
npm create vite@latest my-inertia-app -- --template vue
# For Svelte
npm create vite@latest my-inertia-app -- --template svelte
cd my-inertia-app
Install the necessary dependencies for Fastify and Inertia:
# For React (used in this guide)
npm install fastify-inertiajs @fastify/cookie @fastify/session @fastify/static @inertiajs/react
# For Vue
npm install fastify-inertiajs @fastify/cookie @fastify/session @fastify/static @inertiajs/vue3
# For Svelte
npm install fastify-inertiajs @fastify/cookie @fastify/session @fastify/static @inertiajs/svelte
# Additional dev dependencies
npm install -D nodemon
Set up your project structure as follows:
my-inertia-app/
├── build/ # Generated build artifacts
├── public/ # Static assets
├── src/
│ ├── pages/ # Inertia page components
│ ├── assets/ # Styles, images, etc.
│ ├── main.jsx # Client entry point (or .js/.vue/.svelte)
│ └── ssr.jsx # SSR entry point (optional)
├── index.html # HTML template
├── vite.config.js # Vite configuration
├── server.js # Express server
└── package.json
server.js
)import fastify from "fastify";
import fastifyStatic from "@fastify/static";
import fastifyCookie from "@fastify/cookie";
import fastifySession from "@fastify/session";
import inertia from "fastify-inertiajs";
import path from "path";
import { fileURLToPath } from "url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function bootstrap() {
const app = fastify();
const PORT = process.env.PORT || 5000;
// Serve static files in production
if (process.env.NODE_ENV === "production") {
await app.register(fastifyStatic, {
root: path.join(__dirname, "build/client"),
prefix: "/",
decorateReply: false,
});
}
await app.register(fastifyCookie);
await app.register(fastifySession, {
secret:
process.env.SESSION_SECRET ||
"a secret with minimum length of 32 characters",
saveUninitialized: true,
cookie: {
maxAge: 1000 * 60 * 60 * 24,
secure: process.env.NODE_ENV === "production",
},
});
await app.register(inertia, {
rootElementId: "root",
assetsVersion: "v1",
ssrEnabled: true,
ssrEntrypoint: "src/ssr.jsx",
ssrBuildEntrypoint: "build/ssr/ssr.js",
});
app.get("/", (req, reply) => {
reply.inertia.render("home");
});
try {
await app.listen({ port: PORT });
console.log(`Server is running at http://localhost:${PORT}`);
} catch (err) {
console.error(err);
process.exit(1);
}
}
bootstrap().catch(console.error);
{
"scripts": {
"dev": "nodemon server.js",
"start": "cross-env NODE_ENV=production node server.js",
"build": "npm run build:ssr && npm run build:client",
"build:client": "vite build --outDir build/client",
"build:ssr": "vite build --outDir build/ssr --ssr src/ssr.jsx"
}
}
Update your framework's main entry point accordingly. For more details, visit Inertia.js Client-Side Setup:
import { createInertiaApp } from "@inertiajs/react";
import { createRoot } from "react-dom/client";
createInertiaApp({
id: "root",
resolve: (name) => {
const pages = import.meta.glob("./pages/**/*.jsx", { eager: true });
return pages[`./pages/${name}.jsx`];
},
setup({ el, App, props }) {
createRoot(el).render(<App {...props} />);
},
});
Add Server-Side Rendering support for improved SEO and performance.
import ReactDOMServer from "react-dom/server";
import { createInertiaApp } from "@inertiajs/react";
export default function render(page) {
return createInertiaApp({
id: "root",
page,
render: ReactDOMServer.renderToString,
resolve: (name) => {
const pages = import.meta.glob("./pages/**/*.jsx", { eager: true });
return pages[`./pages/${name}.jsx`];
},
setup: ({ App, props }) => <App {...props} />,
});
}
Option | Type | Default | Description |
---|---|---|---|
rootElementId |
string? |
"app" |
DOM element ID where the Inertia app mounts |
assetsVersion |
string? |
"v1" |
Version string used for inertia |
encryptHistory |
boolean? |
true |
Encrypts the Inertia history state for security |
indexEntrypoint |
string? |
"index.html" |
Path to your base HTML template (used in dev mode) |
indexBuildEntrypoint |
string? |
"build/client/index.html" |
Path to the built client HTML entrypoint (used in production) |
ssrEnabled |
boolean? |
false |
Enables/disables server-side rendering (SSR) |
ssrEntrypoint |
string? |
Required if ssrEnabled: true |
Path to your SSR entry file (used in development) |
ssrBuildEntrypoint |
string? |
Required if ssrEnabled: true |
Path to the built SSR bundle (used in production) |
vite |
ViteResolveConfig? |
{ server: { middlewareMode: true }, appType: "custom" } |
Passes custom options to the Vite dev server |
inertia(config?, vite?)
Initializes and returns the Express middleware.
await fastify.register(inertia, inertiaConfig);
reply.inertia.render(component, props?)
Renders an Inertia page component.
fastify.get('/users', (request, reply) => {
const users = await User.findAll();
reply.inertia.render('user/index', {
users: users,
page: req.query.page || 1
});
});
reply.inertia.share(data)
Shares data with the current and subsequent requests.
reply.inertia.share({
auth: {
user: req.user,
permissions: req.user?.permissions,
},
});
reply.inertia.redirect(urlOrStatus, url?)
Redirects the user to a different location while preserving Inertia’s client-side navigation.
fastify.get("/home", (req, reply) => {
// Redirect with default status (302 Found)
reply.inertia.redirect("/dashboard");
// Redirect with explicit status
reply.inertia.redirect(301, "/new-home");
});
We welcome contributions! Please feel free to submit issues, feature requests, or pull requests.
Fork the repository
Create your feature branch:
git checkout -b feat/amazing-feature
Commit your changes with a descriptive message:
git commit -m "feat: add amazing feature"
Push to your branch:
git push origin feat/amazing-feature
Open a Pull Request
If your contribution introduces a breaking change (e.g. changes to configuration options, API methods, or default behavior), please open an issue or discussion first before submitting a PR. This ensures we can:
This project is licensed under the MIT License - see the LICENSE file for details.