Layouts | RedwoodSDK
RedwoodSDK
Frontend Development

Layouts

How to create and use layouts in your RedwoodSDK project


RedwoodSDK provides a powerful layout() function for creating shared UI layouts across your routes. This allows you to maintain consistent page structures, implement nested layouts, and avoid code duplication.

Key Features

  • Composable: Works seamlessly with existing prefix(), render(), and route() functions
  • Nested Support: Multiple layout() calls create properly nested component hierarchies
  • SSR/RSC Safe: Automatic client component detection prevents serialization errors
  • Middleware Friendly: Preserves middleware functions in route arrays

Example with Code

Create a layout component:

src/app/layouts/AppLayout.tsx
import type { LayoutProps } from 'rwsdk/router'

export function AppLayout({ children, requestInfo }: LayoutProps) {
  return (
    <div className="app">
      <header>
        <nav>
          <a href="/">Home</a>
          <a href="/about">About</a>
        </nav>
        {requestInfo && (
          <span>Path: {new URL(requestInfo.request.url).pathname}</span>
        )}
      </header>
      <main>{children}</main>
      <footer>&copy; {new Date().getFullYear()}</footer>
    </div>
  );
}

Use the layout in your routes:

src/app/worker.tsx
import { layout, route, render } from 'rwsdk/router'
import { AppLayout } from './layouts/AppLayout'
import HomePage from './pages/HomePage'
import AboutPage from './pages/AboutPage'

export default defineApp([
  render(Document, [
    layout(AppLayout, [
      route("/", HomePage),
      route("/about", AboutPage),
    ])
  ])
])

Create nested layouts:

src/app/layouts/AdminLayout.tsx
import type { LayoutProps } from 'rwsdk/router'

export function AdminLayout({ children }: LayoutProps) {
  "use client" // Client component example

  return (
    <div className="admin-panel">
      <aside>Admin Sidebar</aside>
      <div className="admin-content">{children}</div>
    </div>
  );
}

Combine layouts with other router functions:

src/app/worker.tsx
export default defineApp([
  render(Document, [
    layout(AppLayout, [
      route("/", HomePage),
      prefix("/admin", [
        layout(AdminLayout, [
          route("/", AdminDashboard),
          route("/users", UserManagement),
        ])
      ])
    ])
  ])
])

Nesting Order

Layouts are applied with outer layouts first. For example:

layout(Outer, [layout(Inner, [route("/", Page)])])
// Results in: <Outer><Inner><Page /></Inner></Outer>

Layout Props

Layout components receive two props:

  • children: The wrapped route content
  • requestInfo: Request context (only passed to server components)

There's a specific type for the LayoutProps prop:

src/app/layouts/AppLayout.tsx
import type { LayoutProps } from 'rwsdk/router'

export function AppLayout({ children, requestInfo }: LayoutProps) {
...

Client Components

The layout() function automatically detects client components and only passes requestInfo to server components to prevent serialization errors.

Complex Composition

Each of these examples work:

src/app/worker.tsx
prefix("/api", layout(ApiLayout, routes))          // ✅
layout(AppLayout, prefix("/admin", routes))        // ✅
render(Document, layout(AppLayout, routes))        // ✅
``