A custom-label storefront for the Norwegian market — name labels, mini labels, wall decor — built on Next.js App Router and TypeScript, with a configurator and a catalogue that changes constantly.
E-commerce · Next.js App Router
Lappeland
A Norwegian custom-label store whose traffic doubled after a rendering rebuild.
- Product
- Lappeland — custom-label storefront for the Norwegian market
- Role
- Senior frontend engineer, contract
- Scope
- Next.js App Router build, SSR and ISR rendering architecture, Core Web Vitals tuning

The tension
Doubling traffic was a rendering problem before it was an interface problem.
Discoverability depended on what the server sent, not on what the client could assemble afterwards — so the rendering strategy became the lever, and the interface followed it.
More organic traffic reaching product pages, and Core Web Vitals inside budget on mobile rather than on a desktop test rig.
A live catalogue could not wait on a full rebuild to publish a change, which ruled out static generation on its own.
Measured after the rendering rebuild shipped.
Held on mobile, not on a desktop test rig.
The product story
The storefront, one rendering decision at a time.
Server-rendered
The first paint is the whole pitch.
The landing view ships from the server, so the product grid is content on arrival rather than a skeleton waiting on JavaScript.

Mobile-first
Built at the narrow width first.
Tailwind layouts start mobile and widen, verified across browsers and viewports against the Figma designs.

Incrementally static
Fresh pages without a rebuild.
ISR lets catalogue pages regenerate on their own schedule, so an edit publishes without redeploying the site.

Walkthrough
The storefront in motion
Recorded from the live site.
The trust test
The happy path is only half the product.
Stock moves between regenerations
Anything reflecting live availability is server-rendered on request, so the incrementally regenerated page is never the one asked to be honest about stock.
The client bundle never arrives
The catalogue was rendered on the server, so it stays readable and indexable when the JavaScript that would have assembled it does not run.
There is no pointer
Keyboard navigation, focus management and screen-reader testing were scoped work rather than a pass at the end, so the storefront is operable without a mouse.
The decisions
Three moves carried the story.
Render on the server
SSR and ISR through the App Router, so pages are content when they arrive and stay fresh without a rebuild — the change that doubled traffic after launch.
Split what the browser has to parse
Code splitting and Core Web Vitals tuning, so the interactive weight of a page tracked what the page actually needed.
Treat accessibility as scope, not polish
WCAG 2.1 AA deliberately rather than incidentally: ARIA roles, keyboard navigation, focus management, contrast audits and screen-reader testing.
What I would test next
