Docs

Migrating from a meta-framework

Nifra is framework-agnostic, so you keep your UI library and replace the meta-framework around it: Next.js → Nifra + React, Nuxt → Nifra + Vue, SvelteKit → Nifra + Svelte, SolidStart → Nifra + Solid. The concepts map one-to-one, and you can migrate incrementally — stand up Nifra's API first, point your existing app at it, then move routes over.

How the concepts map

Meta-framework conceptNifra
File routes (`pages/`, `app/`, `routes/`)routes/ — `.tsx` / `.vue` / `.svelte` / `.mdx`, same dynamic [param] / [...catch-all] conventions
`getServerSideProps` · `load` · `createAsync` · `asyncData`export async function loader() — runs on the server, typed into the page
API routes (`pages/api`, `+server.ts`, route handlers)a server() backend + the typed client — no fetch() wrappers
Layouts (`layout.tsx`, `+layout`, `app.vue`)_layout.tsx — nested layout chains
Form actions / route handlers for mutationsexport async function action() — typed, progressive-enhancement forms
`getStaticProps` / `prerender` / `export const prerender`export const prerender = true (SSG) + ISR via withISR
`<Link>` · `<NuxtLink>` · `<a data-sveltekit-preload>`Nifra's client router — `Link` + hover/focus prefetch, scroll restoration
`next/image` · `nuxt/image`<Image> from @nifrajs/web-<fw>/image
`metadata` / `<Head>` / `definePageMeta`export const meta (or a meta() function of the loader data)

Data loading (Next.js → Nifra + React)

The biggest change: data fetching becomes a typed loader that calls your backend in-process during SSR — no fetch to your own API, no untyped JSON.

TSX
// Next.js — app/users/[id]/page.tsx
export default async function Page({ params }) {
  const res = await fetch(`https://api/users/${params.id}`)
  const user = await res.json()
  return <h1>{user.name}</h1>
}

// nifra — routes/users/[id].tsx
export async function loader({ params, api }: LoaderArgs<typeof backend>) {
  const res = await api.users({ id: params.id }).get()   // typed, in-process during SSR
  return { user: res.data }
}
export default function User({ data }: { data: LoaderData<typeof loader> }) {
  return <h1>{data.user?.name}</h1>
}

Svelte / Solid / Vue

Identical shape — only the page file's extension and component syntax change. The loader/action/meta exports are the same on every framework (that's Nifra's render seam). SvelteKit's +page.server.ts load, for example:

TS
// SvelteKit — +page.server.ts + +page.svelte
export async function load({ params }) { return { post: await getPost(params.slug) } }

// nifra — routes/blog/[slug].svelte (loader is a module export, page is the .svelte)
export async function loader({ params }) { return { post: await getPost(params.slug) } }

What Nifra adds

  • One model, any UI library — switch React→Solid later by changing one import, not your app.
  • Much faster SSR — Nifra renders ~22× Next.js, ~7× Nuxt, ~3× SvelteKit/SolidStart on dynamic pages, with a fraction of the client JS. See benchmarks.
  • End-to-end types with no codegen, the same app on Bun / Node / Deno / the edge, and a backend you can also ship on its own. Start with Getting started.

Moving a backend (Express, Hono, Fastify, Elysia) instead? See Migrating a backend.

Proudly built with Nifra — server-rendered on Cloudflare Pages.MIT