Layouts and Pages

Learn how to create your first pages and layouts, and link between them with the Link component.

Bini.js uses file-system based routing, meaning you can use folders and files to define routes. This page will guide you through how to create layouts and pages, and link between them.

Creating a page

A page is UI that is rendered on a specific route. To create a page, add a page file inside the app directory and default export a React component. For example, to create an index page (/):

app/page.tsx
// src/app/page.tsx
export default function Page() {
  return <h1>Hello Bini.js!</h1>
}

Creating a layout

A layout is UI that is shared between multiple pages. On navigation, layouts preserve state, remain interactive, and do not rerender.

You can define a layout by default exporting a React component from a layout file. The component should return an <Outlet /> where child routes will render.

For example, to create a root layout:

app/layout.tsx
// src/app/layout.tsx
export const metadata = {
  title: 'My App',
  description: 'Built with Bini.js',
}

export default function RootLayout() {
  return <Outlet />
}

The layout above is called a root layout because it's defined at the root of the app directory. The root layout is required.

Note: The <html> and <body> tags are defined in index.html.

Creating a nested route

A nested route is a route composed of multiple URL segments. For example, the /blog/[slug] route is composed of three segments:

  • / (Root Segment)
  • blog (Segment)
  • [slug] (Leaf Segment)

In Bini.js:

  • Folders are used to define the route segments that map to URL segments.
  • Files (like page and layout) are used to create UI that is shown for a segment.

To create nested routes, you can nest folders inside each other. For example, to add a route for /blog, create a folder called blog in the app directory. Then, to make /blog publicly accessible, add a page.tsx file:

app/blog/page.tsx
// src/app/blog/page.tsx
export default function Blog() {
  return <h1>Blog</h1>
}

You can continue nesting folders to create nested routes. For example, to create a route for a specific blog post, create a new [slug] folder inside blog and add a page file:

app/blog/[slug]/page.tsx
// src/app/blog/[slug]/page.tsx
export default function BlogPost() {
  return <h1>Hello, Blog Post Page!</h1>
}

Nesting layouts

By default, layouts in the folder hierarchy are also nested. You can nest layouts by adding layout inside specific route segments (folders).

For example, to create a layout for the /dashboard route, add a new layout file inside the dashboard folder.

app/dashboard/layout.tsx
// src/app/dashboard/layout.tsx
export const metadata = {
  title: 'Dashboard',
}

export default function DashboardLayout() {
  return (
    <div className="dashboard">
      <aside>Sidebar</aside>
      <main><Outlet /></main>
    </div>
  )
}

Creating a dynamic segment

Dynamic segments allow you to create routes that are generated from data. To create a dynamic segment, wrap the folder name in square brackets: [segmentName].

app/blog/[slug]/page.tsx
// src/app/blog/[slug]/page.tsx
import { useParams } from 'react-router-dom'

export default function BlogPostPage() {
  const { slug } = useParams()
  
  return (
    <div>
      <h1>Post: {slug}</h1>
    </div>
  )
}

Linking between pages

Use the <Link> component (auto-imported) to navigate between routes. It extends the HTML <a> tag to provide client-side navigation.

app/blog/page.tsx
// src/app/blog/page.tsx
export default function BlogList() {
  const posts = [
    { slug: 'hello-world', title: 'Hello World' },
    { slug: 'getting-started', title: 'Getting Started' },
  ]

  return (
    <ul>
      {posts.map((post) => (
        <li key={post.slug}>
          <Link to={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  )
}

Tip: You can also use useNavigate for programmatic navigation.