Routing
Split the app into routes with a shared layout, links with shortcuts and history.
In this chapter
- Files are routes
- A shared layout
- Going back
- Dynamic segments
The App Router maps files in app/ to URLs, exactly like Next.js. The browser's address bar and history stay in sync, so back, forward and deep links work while the UI renders in the console.
Files are routes
Move the list into its own route and add a settings page:
app/ layout.tsx page.tsx → /Added: deployments/Added: page.tsx → /deploymentsAdded: [id]/page.tsx → /deployments/dpl_8fK2Added: settings/Added: page.tsx → /settingsA shared layout
layout.tsx wraps every page and keeps its state across navigation. Put the top bar and the navigation in it.
Added: import type { ReactNode } from "react";Added: import { Box, Spacer, Text } from "@textjs/next";Added: import Link from "@textjs/next/link";Added: import { usePathname } from "@textjs/next/navigation";Added: Added: export const metadata = { title: { default: "Acme Deploys", template: "%s · Acme" } };Added: Added: export default function RootLayout({ children }: { children?: ReactNode }) {Added: const pathname = usePathname();Added: return (Added: <Box className="flex-col grow">Added: <Box className="flex-row h-1 px-1 gap-2">Added: <Text bold>▼ Acme</Text>Added: <Text color="subtle">{`acme.local${pathname === "/" ? "" : pathname}`}</Text>Added: <Spacer />Added: </Box>Added: <Box className="flex-row h-1 px-1 gap-3">Added: <Link href="/" shortcut="1">Overview</Link>Added: <Link href="/deployments" shortcut="2">Deployments</Link>Added: <Link href="/settings" shortcut="3">Settings</Link>Added: </Box>Added: <Box className="flex-col grow p-1">{children}</Box>Added: </Box>Added: );Added: }shortcut binds a single key to the link. Press 1, 2 and 3 in the console; the URL of a real app would change with each one.
Going back
useRouter() navigates from code. Wire Backspace to router.back() in the layout:
Removed: import { Box, Spacer, Text } from "@textjs/next";Added: import { Box, Spacer, Text, useInput } from "@textjs/next";import Link from "@textjs/next/link";Removed: import { usePathname } from "@textjs/next/navigation";Added: import { usePathname, useRouter } from "@textjs/next/navigation";export default function RootLayout({ children }: { children?: ReactNode }) { const pathname = usePathname();Added: const router = useRouter();Added: Added: useInput((e) => {Added: if (e.key === "backspace") router.back();Added: });Dynamic segments
[id] folders receive their value in params:
import { Text, type PageProps } from "@textjs/next";export default function DeploymentPage({ params }: PageProps<{ id: string }>) { return <Text bold>{params.id}</Text>;}Static hosts need a single SPA rewrite to serve deep links. Chapter 10 covers it.
Try it
This is Acme Deploys at the end of the chapter, running in DevTools. Click the pad, then use the keys below.
Open the console
The app renders in DevTools. Then click here and press 2.
Open on a desktop
This demo renders in the Chrome DevTools console, which needs a desktop browser.
Quiz
2 questionsAnswer every question to complete the chapter.