Migrating from Remix and React Router
These tables map the form and mutation features of Remix and React Router to Silgi. They cover forms and handlers only; the tables on pages, data and the server in Migrating from Nuxt apply to any app. none means Silgi has no equivalent today.
The shape is one thing in Silgi: a handler, a typed function that runs on the server, exported from server/index.ts. A form posts to it (Form action={fn}), a component calls it (useAction(fn), useAsyncData(fn, input)), and with a route a URL answers it. It works without JavaScript, it is a public endpoint that must check who calls it, and it needs no directive.
The route types (+types, href) and Vite's import.meta.env and modes are in Types and environment.
| Remix, React Router | Silgi | Differences |
|---|---|---|
export async function action({ request }) in a route | a handler exported from server/index.ts | the page posts to it with <Form action={fn}> |
await request.formData() | input of the handler | decoded by formToObject |
return data({ errors }, { status: 400 }) | return fail(400, { errors }) | |
redirect("/x") | redirect("/x") | |
<Form method="post" action reloadDocument replace> | <Form action={fn} reloadDocument replace> | reset, instance, refresh and restore are added; Form posts through a handler or navigates with GET |
useActionData() | useAction(fn).state, .failure, .error | readable anywhere in the app, not only in the route |
useNavigation() | useNavigation() | state is idle or loading, plus revalidation; the fields of a running submission are in pendingRuns(app) |
useFetcher() with fetcher.Form, submit, load, state, data, formData | useAction(fn, { instance }) and <Form action={fn} instance={key}> | a keyed run that does not navigate; useForm(fn).for(key) reads one |
useFetchers() | pendingRuns(app) from silgi/adapter | |
useSubmit() | useAction(fn).run(input) | a JSON call, not a form post |
useRevalidator() | useRouter().refresh() and useNavigation().revalidation | |
shouldRevalidate({ defaultShouldRevalidate }) | export const shouldRefresh in the page | defaultShouldRefresh for defaultShouldRevalidate, plus currentUrl and nextUrl |
optimistic UI from fetcher.formData | useOptimistic(value, reduce) | pending lists the running calls, each with its formData |
a hidden intent field and a switch in action | one handler per intent | |
clientAction, clientLoader | none | handlers run on the server; browser-only data is useAsyncData(key, fn, { server: false }) |
unstable_parseMultipartFormData | form: { onFile } | |
| resource routes | a handler with a route; raw: true for a plain endpoint | |
loader and useLoaderData | useAsyncData(fn, input), or x.load.ts and useData() | load for a status decided before render |
useFormAction | none | Form resolves its own action |