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 tofalseto hide the hero section entirely (nav still shows).withFooter— Set tofalseto 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
- Create
src/app/[locale]/<path>/page.tsx - Add an entry to
baseRouteConfigsinsrc/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:
- Reads the current pathname via
usePathname() - Normalizes legacy URL patterns (e.g.,
/en-gb/student-bills-packagesto/uk/bills) - Looks up the matching
RouteConfigfrom therouteConfigsmap - Checks
allowedRegions— if the current locale is not allowed, redirects to the locale home page - Passes the resolved config to
HeaderLayout - Renders a
LocaleBannerabove 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:
- Navigation — Always rendered at the top
- Hero section — Rendered when
withHeaderis true (ormainSectionis provided)- If
mainSectionis set, it replaces the entire default hero - Otherwise, renders the standard two-column layout with
title,subtext,cta, andrightContent
- If
- Main content — The page’s
children - Footer — Rendered when
withFooteris true (default)
Hero elements use Motion (Framer Motion) for staggered fade-in animations.
Navigation
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 hrefdropdown— Mega-menu dropdown with title, description, image, and sub-itemsfeatures— UK-only features dropdown (Roomie, Bills)
Use getNavItemsForLocale(config, locale) to filter nav items for the current locale.