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 concept | Nifra |
|---|---|
| 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 mutations | export 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.
// 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:
// 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.