npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

encore-inertia

v0.1.1

Published

Inertia.js adapter for Encore.ts applications

Readme

encore-inertia

Inertia.js adapter for Encore.ts applications. Build server-driven single-page apps with Encore.ts on the backend and React (via Inertia) on the frontend.

This library handles the Inertia protocol — full HTML responses on first visit, JSON responses on subsequent navigations — and integrates with Vite for asset loading in both development and production.

Claude Code Skill

If you use Claude Code, you can install this package's skill to give Claude knowledge of the encore-inertia API:

npx skills add simon-johansson/encore-inertia@encore-inertia

Installation

npm install encore-inertia

For the React client helper, also install these peer dependencies:

npm install react react-dom @inertiajs/react

For Vite + React build tooling:

npm install -D vite @vitejs/plugin-react

Quick Start

1. Create an Encore service

// frontend/encore.service.ts
import { Service } from "encore.dev/service";

export default new Service("frontend");

2. Configure the adapter

Create a setup file where you configure the Inertia adapter once:

// frontend/inertia-setup.ts
import { createInertiaAdapter } from "encore-inertia";

export const inertia = createInertiaAdapter({
  viteEntry: "frontend/src/app.tsx",
  title: "My App",
});

3. Define page routes

Use Encore's raw endpoints to serve Inertia pages:

// frontend/pages.ts
import { api } from "encore.dev/api";
import { inertia } from "./inertia-setup";
import Home from "./src/pages/Home";
import About from "./src/pages/About";

export const home = api.raw(
  { expose: true, method: "GET", path: "/" },
  async (req, res) => {
    inertia.render(req, res, Home, { greeting: "Hello world!" });
  },
);

export const about = api.raw(
  { expose: true, method: "GET", path: "/about" },
  async (req, res) => {
    inertia.render(req, res, About);
  },
);

The render function is generic — it infers the props type from the component you pass. If Home expects { greeting: string }, TypeScript will require you to pass those props. If About takes no props, the argument is optional.

4. Serve static assets

Serve Vite's build output using Encore's static asset support:

// frontend/static.ts
import { api } from "encore.dev/api";

export const assets = api.static({
  expose: true,
  path: "/assets/*path",
  dir: "./dist/assets",
});

Note: Encore requires the static assets directory to exist when starting the application. Before running encore run for the first time, create it with:

mkdir -p frontend/dist/assets

After that, running npm run build (Vite) will populate the directory automatically.

5. Set up the React client

// frontend/src/app.tsx
import { mountInertiaApp } from "encore-inertia/react";

mountInertiaApp({
  pages: import.meta.glob("./pages/**/*.tsx", { eager: true }),
});

6. Create page components

// frontend/src/pages/Home.tsx
import { Link } from "@inertiajs/react";

interface HomeProps {
  greeting: string;
}

export default function Home({ greeting }: HomeProps) {
  return (
    <div>
      <h1>{greeting}</h1>
      <Link href="/about">About</Link>
    </div>
  );
}
// frontend/src/pages/About.tsx
import { Link } from "@inertiajs/react";

export default function About() {
  return (
    <div>
      <h1>About</h1>
      <Link href="/">Home</Link>
    </div>
  );
}

7. Configure Vite

// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  build: {
    manifest: true,
    outDir: "frontend/dist",
    rollupOptions: {
      input: "frontend/src/app.tsx",
    },
  },
  server: {
    origin: "http://localhost:5173",
    cors: {
      origin: "http://localhost:4000",
    },
  },
});

8. Add scripts to package.json

{
  "scripts": {
    "build": "vite build",
    "dev:frontend": "vite dev"
  }
}

Running

Development (with hot module replacement):

# Terminal 1: Start Encore backend
encore run

# Terminal 2: Start Vite dev server
npm run dev:frontend

The adapter automatically falls back to the Vite dev server when no production manifest is found.

Production:

# Build frontend assets
npm run build

# Start Encore
encore run

Configuration

createInertiaAdapter accepts a config object:

const inertia = createInertiaAdapter({
  // Required: key in the Vite manifest matching your entry file
  viteEntry: "frontend/src/app.tsx",

  // Optional (shown with defaults)
  title: "Encore App",                                    // HTML <title>
  manifestPath: "frontend/dist/.vite/manifest.json",      // Vite manifest location
  devServerUrl: "http://localhost:5173",                   // Vite dev server URL
  rootId: "app",                                          // Mount point element ID
  lang: "en",                                             // HTML lang attribute
  version: "1.0",                                         // Inertia protocol version
  head: "",                                               // Extra HTML for <head>
});

Custom HTML template

For full control over the HTML shell, provide a renderHtml function. When set, it overrides title, lang, head, and rootId:

const inertia = createInertiaAdapter({
  viteEntry: "frontend/src/app.tsx",
  renderHtml: (page, assetTags) => `<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    ${assetTags}
</head>
<body>
    <div id="app" data-page='${JSON.stringify(page)}'></div>
</body>
</html>`,
});

The function receives:

  • page — the Inertia page object ({ component, props, url, version })
  • assetTags — pre-built <script> and <link> tags from the Vite manifest

React client options

mountInertiaApp({
  // Required: result of import.meta.glob with eager: true
  pages: import.meta.glob("./pages/**/*.tsx", { eager: true }),

  // Optional (shown with default)
  rootId: "app",  // Must match the rootId in your adapter config
});

Shared data

Use share() to define props that are automatically merged into every page response for a given request. This is useful for data that many pages need, like the authenticated user or flash messages.

export const home = api.raw(
  { expose: true, method: "GET", path: "/" },
  async (req, res) => {
    inertia.share(req, { user: { name: "Alice" } });
    inertia.share(req, { flash: { success: "Welcome back!" } });

    inertia.render(req, res, Home, { greeting: "Hello!" });
    // props = { user: { name: "Alice" }, flash: { success: "Welcome back!" }, greeting: "Hello!" }
  },
);
  • Call share() one or more times before render() — shared data accumulates across calls
  • Page-level props passed to render() take precedence over shared props
  • Shared data is scoped to the request and does not leak between requests

API Reference

createInertiaAdapter(config): InertiaAdapter

Creates an adapter instance. Returns:

  • render<P>(req, res, component, props?) — Handles the Inertia protocol. Takes a component function (not a string name) and infers the props type P from it. Props are required when the component expects them and optional when it doesn't. On first visit (no X-Inertia header), responds with a full HTML page. On subsequent navigations, responds with a JSON page object.
  • share(req, data) — Merges data into shared props for this request. Can be called multiple times — entries accumulate. Shared props are merged under page props when render() is called, with page-level props taking precedence.
  • getAssetTags() — Returns the <script> and <link> tags string. In production, reads from the Vite manifest. In development, points to the Vite dev server.

mountInertiaApp(config): void

Import from encore-inertia/react

Wraps @inertiajs/react's createInertiaApp with automatic page component resolution from Vite's import.meta.glob output.

Project Structure

A typical Encore + Inertia project:

my-app/
├── encore.app
├── package.json
├── vite.config.ts
└── frontend/
    ├── encore.service.ts        # Encore service definition
    ├── inertia-setup.ts         # Adapter configuration
    ├── pages.ts                 # Page route endpoints
    ├── static.ts                # Static asset serving
    ├── src/
    │   ├── app.tsx              # React/Inertia client entry
    │   └── pages/
    │       ├── Home.tsx         # Page components
    │       └── About.tsx
    └── dist/                    # Vite build output (gitignored)

How It Works

  1. A browser request hits an Encore raw endpoint
  2. The endpoint calls inertia.render(req, res, ComponentFunction, { props })
  3. First visit (no X-Inertia header): the adapter responds with a full HTML page containing the Vite asset tags and the page object embedded in a data-page attribute
  4. Subsequent navigation (X-Inertia header present): the adapter responds with just the JSON page object, and the Inertia client-side library swaps the page component without a full reload

License

MPL-2.0