Information architecture before design: the order UK SMB sites ship in
You don’t design a building by deciding what colour the front door is. You decide where the load-bearing walls go, then where the rooms sit, then what they’re for, then how they connect, then — last — what the place looks like. A surveyor at Loughborough taught me that order in 2014 and I’ve never seen a website project that didn’t benefit from it.
The default UK agency workflow inverts the whole thing. Figma opens on day one. The homepage hero gets mocked. The client signs off “the look.” Then the developer is handed a 40-frame design file and asked to make it real on a CMS that can’t produce the content shapes the hero needs. Six weeks of rework follow. Two years later the site needs replacing because the data model never matched the business.
This piece walks through the correct order — routes → render contract → content model → components → visuals — through a real Foundation-tier build, and shows what each artefact looks like in the codebase.
1. The URL graph — before anything else
The first artefact is a routes.ts-style URL map. Not a sitemap.xml — a TypeScript file that names every page the site will serve, what slug pattern it uses, and what kind of resource lives at it.
For a Leeds private clinic running fourteen treatment service pages, the URL graph looks like this:
// src/lib/routes.ts
export const routes = {
home: "/",
about: "/about",
services: "/services",
service: (slug: string) => `/services/${slug}`,
team: "/team",
teamMember: (slug: string) => `/team/${slug}`,
contact: "/contact",
booking: "/booking",
bookingConfirm: "/booking/confirm",
pricing: "/pricing",
legal: { privacy: "/privacy", terms: "/terms" },
} as const;
Boring. That’s the point. The URL graph is the commitment — to Google, to the client, to your future self — that these URLs exist and won’t change. Every page on the site has a stable canonical address before a single component is built.
The commercial value is concrete: the URL graph is what holds search rankings through a redesign. When the visual layer gets replaced two years in, the URLs don’t move, so the backlinks Google has been counting since 2024 still resolve.
2. The render contract — typed before designed
The render contract is the TypeScript interface that says what props every page template needs. It’s the second artefact, and it has to exist before Figma opens.
For the same clinic, the service-page render contract:
// src/templates/ServicePage.ts
export interface ServicePageProps {
slug: string;
title: string; // h1
metaDescription: string;
hero: { headline: string; subhead: string; cta: { label: string; href: string } };
whatItIs: { body: string; bullets: string[] };
whoItIsFor: { body: string; conditions: string[] };
practitioners: Array<{ slug: string; name: string }>;
pricing: { from: number; currency: "GBP"; sessions?: string };
faqs: Array<{ q: string; a: string }>;
related: Array<{ slug: string; title: string }>;
}
This interface is the contract between the designer and the data. Every visual element on a service page is something this object can produce. Nothing else.
The mistake agencies make is letting the designer mock up a hero with three rotating testimonials, a video background, and a live availability calendar — when the CMS the practice will actually run on is Decap (or, worse, a Wix block library) and none of those things have a place to live. The render contract makes that mistake impossible because the contract is checked-in code, not a Figma comment.
3. The content model — schema before copy
The third artefact is the content collection schema. For an Astro site, that’s content.config.ts. For a Sanity/Decap site, it’s the studio schema file. The content model is the database in disguise: every field, every type, every required-vs-optional decision.
A treatment service is not a “page.” It’s a row in a services collection with named fields:
// src/content.config.ts
const services = defineCollection({
schema: z.object({
title: z.string(),
slug: z.string(),
category: z.enum(["aesthetic", "dermatology", "wellness"]),
durationMinutes: z.number(),
priceFromGbp: z.number(),
practitionerSlugs: z.array(z.string()),
bodyMd: z.string(),
faqs: z.array(z.object({ q: z.string(), a: z.string() })).default([]),
}),
});
Now the content model is also typed. Astro will refuse to build if a service record is missing priceFromGbp. That refusal is the system catching a content error at build time, not at 3am when a clinic owner notices the price field is blank on a live page.
The commercial value: a fourteen-page service catalogue isn’t fourteen pages of work. It’s one template, one schema, and fourteen rows. Adding a fifteenth service is a five-minute job, not a four-hour job. Solicitors with a forty-matter-type catalogue, accountants with a thirty-service practice menu, schools with twenty department pages — all the same shape.
4. Components — composed against the contract
Only now — when routes, contracts, and content models exist — do components get built. Each component renders one piece of one contract. The <ServiceHero> component takes props.hero and renders it. It doesn’t reach into the page’s other props. It doesn’t know what a service is. It knows what a hero is.
The discipline is single-purpose components against the contract, not the visual decomposition of a Figma mock. A “hero with three rotating testimonials” is two components: <ServiceHero> and <TestimonialCarousel>. They’re independent. They can be reused on the about page, the team page, the pricing page. The visual is a composition of the system, not a bespoke render.
This is also where progressive enhancement gets enforced. Every component ships as static HTML first; interactivity is layered on top via client:visible islands. The contract doesn’t change when JavaScript breaks. The render still works.
5. Visuals — last
Now, finally, the designer opens Figma. The brief isn’t “design a homepage.” The brief is “design the visual layer of this system.” The system already exists — routes, contracts, content, components — and the visual layer is the surface that wraps it.
This is the inversion that saves the six weeks. The designer can’t mock up a hero that the CMS can’t produce, because the CMS schema is the source the designer is working from. The developer doesn’t get handed an impossible spec. The content team doesn’t get asked to write copy for a slot that doesn’t fit anything they’ve ever written.
I run this with Stripe-docs / Linear-changelog / Vercel-docs as the tonal anchors — calm, restrained, document-like. The visuals serve the system. They don’t fight it.
What goes wrong when the order inverts
Three failure modes, all of them I’ve seen this year on UK SMB sites:
- The orphaned hero. Designer mocks a hero with a “live appointment counter.” Nobody costed the booking-system integration. It ships as a static “Book now” button. The hero looks empty for the next two years.
- The page that doesn’t exist. Designer mocks a “team member detail page” with a video bio. The content model doesn’t have a video field. Half the team members never get their pages built. The site’s information architecture has a missing limb.
- The redesign that breaks SEO. Two years later, a visual refresh changes every URL because the routes were never canonical. Google de-indexes the site for six weeks. The clinic’s organic traffic halves. (See the 0.05-second test for what that traffic was already worth.)
All three are prevented by the build order.
What the order looks like for the three regulated verticals
Clinics. Routes: /services/[slug], /team/[slug], /booking, /contact. Render contract enforces “no analytics on /booking/confirm” (GDPR), “no third-party form on /contact” (ICO posture). Content model has practitionerSlugs so a service knows who delivers it. Visuals last.
Solicitors. Routes: /services/[matter-type], /people/[solicitor], /insights/[slug], /contact. Render contract enforces SRA Transparency Rules — every /services/[matter-type] page must surface a fee structure and complaints route. Content model has feeBasis: "fixed" | "hourly" | "estimate". Visuals last.
Accountants. Routes: /services/[slug], /team/[slug], /resources/[slug], /contact. Render contract enforces “no third-party chat widget anywhere” (ICAEW confidentiality). Content model separates taxYear for any guidance content so 2025/26 advice doesn’t get served as if it’s 2026/27. Visuals last.
The pattern repeats. The order is the same regardless of vertical. The vertical only changes which constraints get baked into the render contract.
Closing — what to do with this
If you’re being quoted by an agency right now, ask three questions:
- “Can I see the URL graph before we sign off the visual?”
- “What does the page template’s TypeScript contract say a service page needs?”
- “Where does the content live, and what’s the schema?”
If the agency can’t answer those three, the build will be visuals-first. Six weeks of rework, then two years of brittleness. If the agency can answer them, you’ve found someone who builds in the right order.
This is how every UK Web Marketing build ships — routes, contracts, content, components, visuals, in that order. Or run a site audit and we’ll show you which order yours was built in.
Pages vs systems, the build order, the inversion: more on the same thread in Pages vs systems: why a website is the wrong unit of design.