Data fetching

Pages can be async functions. The framework awaits them before rendering, which means you can await data directly in your components — no useEffect or data-fetching library required.

Fetching in a page

app/blog/page.tsx
async function getPosts() {
  const res = await fetch("https://api.example.com/posts");
  return res.json();
}

export default async function BlogPage() {
  const posts = await getPosts();
  return (
    <ul>
      {posts.map((p) => <li key={p.id}>{p.title}</li>)}
    </ul>
  );
}

Fetching in a layout

Layouts can also be async. The data is fetched once and shared across all child pages.

Loading UI

For a non-blocking experience, combine loading.tsx with Suspense boundaries:

app/dashboard/page.tsx
import { Suspense } from "react";

export default function DashboardPage() {
  return (
    <Suspense fallback={<p>Loading…</p>}>
      <RecentActivity />
    </Suspense>
  );
}

async function RecentActivity() {
  const data = await fetch("https://api.example.com/activity").then((r) => r.json());
  return <ul>{data.map(...)}</ul>;
}

Route handlers (API routes)

For pure data endpoints, create a route.ts file. Export functions named after HTTP methods:

app/api/posts/route.ts
import type { RouteHandler } from "swift-rust/router";

export const GET: RouteHandler = async ({ request }) => {
  const posts = await db.posts.findMany();
  return Response.json(posts);
};

export const POST: RouteHandler = async ({ request }) => {
  const body = await request.json();
  const post = await db.posts.create(body);
  return Response.json(post, { status: 201 });
};

Next steps

Continue to Styling.