Pages
Creating and organizing pages in Bunbox
Creating a Page
Create a page.tsx file to define a page:
// app/about/page.tsx
export default function AboutPage() {
return (
<div>
<h1>About Us</h1>
<p>Welcome to our about page!</p>
</div>
);
}This creates a page at /about.
Page Props
Pages receive params, query, and data as props:
import type { PageProps } from "@ademattos/bunbox";
export default function Page({ params, query, data }: PageProps) {
return (
<div>
<h1>Params: {JSON.stringify(params)}</h1>
<p>Query: {JSON.stringify(query)}</p>
<p>Data: {JSON.stringify(data)}</p>
</div>
);
}Data Loading with Loaders
Use the loader export for server-side data fetching:
import type { LoaderContext, PageProps } from "@ademattos/bunbox";
// Loader runs on the server
export async function loader({ params, query }: LoaderContext) {
const user = await db.getUser(params.id);
return { user };
}
// Page receives loader data via props.data
export default function UserPage({ params, data }: PageProps) {
const { user } = data as { user: User };
return (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}Loaders run:
- On the server for initial page loads
- On the server via fetch for client-side navigation
Page Metadata
Export metadata for SEO:
import type { PageMetadata } from "@ademattos/bunbox";
export const metadata: PageMetadata = {
title: "About Us",
description: "Learn more about our company",
keywords: ["about", "company"],
};
export default function AboutPage() {
return <div>About Us</div>;
}Interactive Components
All pages are fully hydrated and support React hooks:
import { useState } from "react";
export default function CounterPage() {
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}Combining Loaders with Interactivity
Use loaders for initial data, React state for client interactions:
import { useState } from "react";
import type { LoaderContext, PageProps } from "@ademattos/bunbox";
export async function loader({ params }: LoaderContext) {
const posts = await db.getPosts();
return { posts };
}
export default function BlogPage({ data }: PageProps) {
const { posts } = data as { posts: Post[] };
const [filter, setFilter] = useState("");
const filteredPosts = posts.filter((p) =>
p.title.toLowerCase().includes(filter.toLowerCase())
);
return (
<div>
<input
value={filter}
onChange={(e) => setFilter(e.target.value)}
placeholder="Search posts..."
/>
{filteredPosts.map((post) => (
<article key={post.id}>
<h2>{post.title}</h2>
</article>
))}
</div>
);
}Not Found Pages
Create a custom 404 page:
// app/not-found.tsx
export default function NotFound() {
return (
<div>
<h1>404 - Page Not Found</h1>
<a href="/">Go Home</a>
</div>
);
}