Skip to Content

Layout System

The layout system controls page chrome (header, hero, navigation, footer) via a centralized route configuration. All layout components live in src/components/layouts/.

Route Config

src/components/layouts/route-config.tsx defines a RouteConfig interface and a map of path-to-config entries.

RouteConfig Interface

interface RouteConfig { title?: string | (({ locale }: { locale?: string }) => string); subtext?: string; cta?: string; ctaLink?: string; rightContent?: React.ReactNode; withHero?: boolean; isBillsPage?: boolean; mainSection?: React.ReactNode | (({ dictionary }) => React.ReactNode); withFooter?: boolean; // default: true withHeader?: boolean; // default: true allowedRegions?: string[]; // e.g. ["uk"] or ["us"] customCta?: React.ReactNode | (({ dictionary }) => React.ReactNode); customHeader?: React.ReactNode; customLayoutStyles?: { leftColumnClass?: string; rightColumnClass?: string; containerClass?: string; }; }

Key Fields

  • title — Hero heading text. Can be a string or a function receiving { locale } for locale-aware titles (e.g., “housemates” vs “roommates”).
  • subtext — Hero description paragraph.
  • mainSection — Replaces the entire default hero with a custom component. Can be a function receiving { dictionary }.
  • rightContent — Component rendered on the right side of the hero (typically an image or laptop mockup).
  • cta / ctaLink — Button text and destination link in the hero.
  • customCta — Replaces the default CTA button with a custom component (e.g., ContactFormDialog).
  • withHeader — Set to false to hide the hero section entirely (nav still shows).
  • withFooter — Set to false to hide the footer (used for full-screen flows like bills quote).
  • allowedRegions — Restricts the page to specific locales. If set to ["uk"], US users are redirected away.

Route Config Entries

Routes are defined as bare paths (no locale prefix):

const baseRouteConfigs: Record<string, RouteConfig> = { "/": { title: "The home of student living", subtext: "Streamline the process of finding your next student house...", mainSection: ({ dictionary }) => <HomeHeader dictionary={dictionary || {}} />, }, "/bills": { mainSection: <BillsHeader />, isBillsPage: true, allowedRegions: ["uk"], }, "/bills/quote": { withFooter: false, withHeader: false, allowedRegions: ["uk"], }, "/about": { title: "About", subtext: "At the core of our success...", rightContent: <AboutHeaderRightContent />, }, "/colleges": { title: "College Partners", subtext: "...", rightContent: <CollegesHeaderLaptop />, allowedRegions: ["us"], }, // ... more routes };

These base configs are then expanded into locale-prefixed entries. UK routes get a /uk prefix; US routes remain bare (since US is the default).

Adding a New Page

  1. Create src/app/[locale]/<path>/page.tsx
  2. Add an entry to baseRouteConfigs in src/components/layouts/route-config.tsx:
"/my-new-page": { title: "My New Page", subtext: "Description text for the hero.", allowedRegions: ["us", "uk"], // or just one },

If you skip step 2, the page will render with withHeader: false (no hero section) as a fallback.

SmartHeaderLayout

src/components/layouts/smart-header-layout.tsx is a client component that:

  1. Reads the current pathname via usePathname()
  2. Normalizes legacy URL patterns (e.g., /en-gb/student-bills-packages to /uk/bills)
  3. Looks up the matching RouteConfig from the routeConfigs map
  4. Checks allowedRegions — if the current locale is not allowed, redirects to the locale home page
  5. Passes the resolved config to HeaderLayout
  6. Renders a LocaleBanner above the header

Path Normalization

SmartHeaderLayout handles the mapping between public-facing URLs and internal route config keys:

if (pathname.startsWith("/en-gb/features/roomie")) { normalizedPath = "/uk/roomie"; } else if (pathname.startsWith("/en-gb/student-bills-packages/quote")) { normalizedPath = "/uk/bills/quote"; } else if (pathname.startsWith("/en-gb")) { normalizedPath = "/uk" + pathname.substring(6); } else if (pathname.startsWith("/en-us")) { normalizedPath = pathname.substring(6) || "/"; }

Region Enforcement

If a route has allowedRegions and the current locale is not in the list, SmartHeaderLayout triggers a client-side redirect:

if (!isRouteAllowedInRegion(pathWithoutLocale, locale)) { return { shouldRedirect: true, redirectTo: redirectPrefix }; }

HeaderLayout

src/components/layouts/header-layout.tsx assembles the page chrome from the resolved RouteConfig:

  1. Navigation — Always rendered at the top
  2. Hero section — Rendered when withHeader is true (or mainSection is provided)
    • If mainSection is set, it replaces the entire default hero
    • Otherwise, renders the standard two-column layout with title, subtext, cta, and rightContent
  3. Main content — The page’s children
  4. Footer — Rendered when withFooter is true (default)

Hero elements use Motion (Framer Motion) for staggered fade-in animations.

Navigation is configured in src/components/layouts/navigation-config.ts. Each nav item specifies which locales it appears in:

export const navigationConfig: NavItem[] = [ // UK nav items { id: "rental", type: "link", label: "Rental", href: "/student-accommodation", locales: ["uk"] }, { id: "features", type: "features", label: "Features", locales: ["uk"], features: { uk: [...] } }, { id: "about", type: "link", label: "About", href: "/about", locales: ["uk"] }, { id: "lettings", type: "link", label: "Lettings", href: "/partners/lettings", locales: ["uk"] }, { id: "universities", type: "link", label: "Universities", href: "/partners/universities", locales: ["uk"] }, // US nav items { id: "colleges", type: "dropdown", label: "Colleges", href: "/colleges", locales: ["us"], dropdown: {...} }, { id: "partners", type: "dropdown", label: "Partners", href: "/partners", locales: ["us"], dropdown: {...} }, { id: "students", type: "dropdown", label: "Students", href: "/students", locales: ["us"], dropdown: {...} }, { id: "parents", type: "link", label: "Parents", href: "/parents", locales: ["us"] }, ];

Three nav item types:

  • link — Simple link with href
  • dropdown — Mega-menu dropdown with title, description, image, and sub-items
  • features — UK-only features dropdown (Roomie, Bills)

Use getNavItemsForLocale(config, locale) to filter nav items for the current locale.

Last updated on