{"_id":"@dogbyte-labs/hono-inertiajs","name":"@dogbyte-labs/hono-inertiajs","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@dogbyte-labs/hono-inertiajs","repository":{"type":"git","url":"git+https://github.com/dogbyte-labs/hono-inertiajs.git"},"version":"1.0.0","description":"Hono middleware for Inertia.js","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","default":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.build.json","prepare":"tsc -p tsconfig.build.json","test":"vitest run"},"keywords":["hono","inertia","middleware","typescript"],"author":{"name":"@dogbyte-labs"},"license":"ISC","publishConfig":{"access":"public"},"packageManager":"pnpm@10.33.0","volta":{"node":"24.15.0"},"peer-dependencies":{"hono":"^4.12.14"},"devDependencies":{"typescript":"^6.0.3","vitest":"^4.1.4"},"gitHead":"377178170883e3d449bf392e40160b17c6a929db","_id":"@dogbyte-labs/hono-inertiajs@1.0.0","bugs":{"url":"https://github.com/dogbyte-labs/hono-inertiajs/issues"},"homepage":"https://github.com/dogbyte-labs/hono-inertiajs#readme","_nodeVersion":"24.15.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-Q6ZEgPkaY2Xspjdnc8NHO5yVQ2C7htlVMbf6/Ts2143VE1zbY29hWgbspIY77Vewu9N8QQDmTnDpMVTk6ir8FA==","shasum":"411335f261be520ecb2c771358f831224fd2effb","tarball":"https://registry.npmjs.org/@dogbyte-labs/hono-inertiajs/-/hono-inertiajs-1.0.0.tgz","fileCount":35,"unpackedSize":64240,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIDdiSoySttdkYyk08oW0ZapjKzoFi9AvQOxUhrXHLekbAiEA945e0Mhu6gGl9Cv6D2CnQu7P9j0EnSDeujGmfJFRtCQ="}]},"_npmUser":{"name":"josevelaz","email":"jose.c.velazquez@proton.me"},"directories":{},"maintainers":[{"name":"josevelaz","email":"jose.c.velazquez@proton.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/hono-inertiajs_1.0.0_1776802981083_0.9870203366431747"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-21T20:23:01.009Z","1.0.0":"2026-04-21T20:23:01.231Z","modified":"2026-04-21T20:23:01.448Z"},"maintainers":[{"name":"josevelaz","email":"jose.c.velazquez@proton.me"}],"description":"Hono middleware for Inertia.js","homepage":"https://github.com/dogbyte-labs/hono-inertiajs#readme","keywords":["hono","inertia","middleware","typescript"],"repository":{"type":"git","url":"git+https://github.com/dogbyte-labs/hono-inertiajs.git"},"author":{"name":"@dogbyte-labs"},"bugs":{"url":"https://github.com/dogbyte-labs/hono-inertiajs/issues"},"license":"ISC","readme":"# Hono InertiaJS\n\nHono middleware for Inertia.js. It gives a Hono app Inertia-aware rendering, shared props, version checks, partial reload support, and optional SSR. Fully compatible with **Inertia.js v1 and v3**.\n\n## Installation\n\n```bash\nnpm/pnpm/bun/yarn add @dogbyte-labs/hono-inertiajs hono\n```\n\nIf you are building an Inertia frontend, install the matching adapter too:\n\n```bash\nnpm/pnpm/bun/yarn add @inertiajs/react react react-dom\n# or\nnpm/pnpm/bun/yarn add @inertiajs/vue3 vue\n```\n\n## Usage\n\n### 1) Create a document renderer\n\nThe `document` renderer returns the HTML shell for the first visit.\n\nUse `renderInertiaRoot` (v3+) to generate the Inertia root element automatically — it serialises the page object and sets the correct `id` and `data-page` attributes:\n\n```ts\nimport { Hono } from 'hono'\nimport {\n  inertia,\n  renderInertiaRoot,\n  type InertiaOptions,\n} from '@dogbyte-labs/hono-inertiajs'\n\nconst document: InertiaOptions['document'] = ({ page, ssr }) => {\n  const headTags = ssr?.head?.join('\\n') ?? ''\n\n  // Builds <div id=\"app\" data-page=\"…\">…</div> plus an optional script tag.\n  const rootHtml = renderInertiaRoot(page, {\n    id: 'app',\n    ssr,\n    scriptSrc: '/assets/app.js',\n  })\n\n  return `<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    ${headTags}\n  </head>\n  <body>\n    ${rootHtml}\n  </body>\n</html>`\n}\n```\n\nOr build the root element manually (v1/v2 style):\n\n```ts\nconst document: InertiaOptions['document'] = ({ page, ssr }) => {\n  const appHtml = ssr?.body ?? ''\n  const headTags = ssr?.head?.join('\\n') ?? ''\n\n  return `<!doctype html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"utf-8\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" />\n    ${headTags}\n  </head>\n  <body>\n    <div id=\"app\" data-page='${JSON.stringify(page)}'>${appHtml}</div>\n  </body>\n</html>`\n}\n```\n\n### 2) Mount the middleware\n\n```ts\nconst app = new Hono()\n\napp.use(\n  '*',\n  inertia({\n    document,\n    version: '1.0.0',\n    share: async (c) => ({\n      auth: { user: null },\n    }),\n    resolveErrors: async (_c) => ({}),\n  }),\n)\n```\n\n### 3) Render pages from routes\n\n```ts\napp.get('/users', async (c) => {\n  const users = await getUsers()\n\n  return c.var.inertia.render('Users/Index', { users })\n})\n\napp.get('/logout', (c) => {\n  return c.var.inertia.location('https://example.com')\n})\n```\n\n### 4) Optional per-request shared props\n\n```ts\napp.get('/dashboard', (c) => {\n  c.var.inertia.share({ flash: 'Saved successfully' })\n\n  return c.var.inertia.render('Dashboard', { count: 42 })\n})\n```\n\n## Inertia v3 Support\n\n### Prop wrappers\n\nProp wrappers let you control how individual props are sent to the client.\n\n#### `defer` — lazy / deferred props\n\n```ts\nimport { defer } from '@dogbyte-labs/hono-inertiajs'\n\napp.get('/users', async (c) => {\n  const users = await getUsers()\n\n  return c.var.inertia.render('Users/Index', {\n    // Sent immediately.\n    users,\n\n    // Fetched lazily after the initial page load.\n    // The client receives `undefined` on the first render; Inertia fills it\n    // in with a follow-up request automatically.\n    stats: defer(() => getExpensiveStats()),\n\n    // Optional group name — multiple deferred props in the same group are\n    // fetched together in a single follow-up request.\n    chart: defer(() => getChartData(), 'analytics'),\n  })\n})\n```\n\n#### `merge` / `deepMerge` — incremental / infinite-scroll props\n\n```ts\nimport { merge, deepMerge } from '@dogbyte-labs/hono-inertiajs'\n\napp.get('/feed', async (c) => {\n  const posts = await getPosts()\n\n  return c.var.inertia.render('Feed', {\n    // On subsequent visits the new list is merged (appended) to the\n    // client's existing list rather than replacing it.\n    posts: merge(posts),\n  })\n})\n```\n\n#### `once` — flash / single-delivery props\n\n```ts\nimport { once } from '@dogbyte-labs/hono-inertiajs'\n\napp.get('/dashboard', async (c) => {\n  return c.var.inertia.render('Dashboard', {\n    // Delivered to the client exactly once per key.\n    // On every subsequent visit the prop resolves to `undefined`.\n    flash: once(\n      async () => session.pull('flash'),\n      `flash:${session.id}`,\n    ),\n  })\n})\n```\n\n#### `always` / `optional` / `lazy`\n\n```ts\nimport { always, optional, lazy } from '@dogbyte-labs/hono-inertiajs'\n\n// always — included even on partial reloads that don't request this prop.\n// optional — excluded from partial reloads unless explicitly requested.\n// lazy — alias for optional.\napp.get('/page', (c) =>\n  c.var.inertia.render('Page', {\n    critical: always(() => getCriticalData()),\n    heavy: optional(() => getHeavyData()),\n  }),\n)\n```\n\n### `renderInertiaRoot`\n\nGenerates the Inertia root element HTML string.\n\n```ts\nimport { renderInertiaRoot } from '@dogbyte-labs/hono-inertiajs'\n\nconst html = renderInertiaRoot(page, {\n  id: 'app',         // defaults to \"app\"\n  ssr,               // optional SSR result ({ body, head })\n  scriptSrc: '/assets/app.js', // optional; injects <script type=\"module\">\n})\n```\n\n### `redirect`\n\nTriggers a client-side Inertia redirect (stays within the SPA).\n\n```ts\napp.post('/users/:id/archive', async (c) => {\n  await archiveUser(Number(c.req.param('id')))\n\n  return c.var.inertia.redirect('/users')\n})\n```\n\n### `isPrefetch`\n\nReturns `true` when the request is an Inertia prefetch.  Skip analytics, DB writes, and other side-effects for prefetch requests.\n\n```ts\napp.get('/users', async (c) => {\n  if (!c.var.inertia.isPrefetch()) {\n    await recordPageView(c)\n  }\n\n  return c.var.inertia.render('Users/Index', { users: await getUsers() })\n})\n```\n\n### Precognition middleware\n\nEnable [Laravel Precognition](https://laravel.com/docs/precognition)-style validation for your routes:\n\n```ts\nimport { precognition } from '@dogbyte-labs/hono-inertiajs'\n\napp.post(\n  '/users',\n  precognition({ /* options */ }),\n  async (c) => {\n    // Validation runs on precognition requests and returns early.\n    // Full handler runs only for real submissions.\n  },\n)\n```\n\n### `createInMemoryOnceStore`\n\nFactory for a simple in-memory `OnceStore`.  Works for single-process deployments.  For multi-process or edge deployments implement the `OnceStore` interface backed by Redis or another shared store.\n\n```ts\nimport { createInMemoryOnceStore } from '@dogbyte-labs/hono-inertiajs'\n\nconst store = createInMemoryOnceStore()\n```\n\n## React frontend example\n\n```tsx\nimport { Head } from '@inertiajs/react'\n\ntype User = { id: number; name: string; email: string }\n\ntype Props = {\n  users: User[]\n}\n\nexport default function UsersIndex({ users }: Props) {\n  return (\n    <>\n      <Head title=\"Users\" />\n      <ul>\n        {users.map((user) => (\n          <li key={user.id}>{user.name}</li>\n        ))}\n      </ul>\n    </>\n  )\n}\n```\n\n## Worth noting\n\n- First visits return HTML; Inertia visits return JSON with `X-Inertia: true`.\n- `document` is required and can return a `string` or a `Response`.\n- `version` can be a string or an async function.\n- `resolveErrors()` defaults to `{}`, and `props.errors` is always present.\n- Partial reload headers are supported: `X-Inertia-Partial-Component`, `X-Inertia-Partial-Data`, and `X-Inertia-Partial-Except`.\n- `c.var.inertia.location(url)` returns `409` with `X-Inertia-Location` (hard external redirect).\n- `c.var.inertia.redirect(url)` returns a soft Inertia SPA redirect (v3+).\n- `c.var.inertia.isPrefetch()` returns `true` for Inertia prefetch requests (v3+).\n- `ssr` is optional; if it fails, the adapter falls back to normal rendering.\n- The package is ESM-only and ships compiled output from `dist/`.\n- Prop wrappers (`defer`, `merge`, `once`, etc.) require Inertia.js v3 on the frontend.\n\n## Development\n\n```bash\npnpm install\npnpm test\npnpm build\n```\n\n## Example\n\nSee `examples/basic.ts` for a complete server-side walkthrough.\n\n## License\n\nISC\n","readmeFilename":"README.md","_rev":"1-510ce87422e28f111e12282f6471ee36"}