Practical Examples

Example 1: Todo CRUD

Load todos with SSR, create with a server function, then invalidate the loader.

const listTodos = createServerFn({ method: 'GET' }).handler(() => db.todo.findMany())

const createTodo = createServerFn({ method: 'POST' })
  .validator((input: { title: string }) => input)
  .handler(({ data }) => db.todo.create({ data }))

export const Route = createFileRoute('/todos')({
  loader: () => listTodos(),
  component: TodosPage,
})

Example 2: Protected Dashboard

export const Route = createFileRoute('/dashboard')({
  beforeLoad: ({ context }) => {
    if (!context.user) throw redirect({ to: '/login' })
  },
  loader: ({ context }) => getDashboard({ data: { userId: context.user.id } }),
  component: Dashboard,
})

Example 3: Webhook API

Use a Server Route, not a Server Function, for public webhooks.

Anti-Patterns

Anti-patternBetter
GET mutationPOST Server Function
Component-only authbeforeLoad plus server authorization
Manual search parsingvalidateSearch
Everything in loadersLoader for route data, Query for local server state

Next Steps