hockey club website

Client

HC Čestice

Role

Fullstack — data migration, CMS data model, frontend, tests, CI/CD and deployment.

Year

2026

Stack

Next.js 16, React 19, Payload CMS 3, PostgreSQL, TypeScript, Tailwind CSS, Playwright, Vitest, Docker

Next.js 16React 19Payload CMS 3PostgreSQLTypeScriptTailwind CSSPlaywrightVitestDocker

Project

HC Čestice is an ice-hockey club whose website carried news, fixtures and results including the amateur hockey league, the player roster, photo galleries, club history, sponsors and club merchandise. The old site was outdated and hard to maintain, so I migrated it completely to a modern CMS.

The most important thing was to lose nothing. I wrote an XML export parser and an import pipeline for articles, sections, photo galleries and files, plus seed scripts for players, fixtures, AHL rosters, history milestones, sponsors and products. A redirect generator maps the legacy paths onto the new pages, so the migration cost the club no inbound links or search rankings.

I chose Payload CMS 3 because it runs inside Next.js. I wrapped an immature navigation plugin in an adapter that fails the build when the plugin changes, I cache navigation by tag, and I extended the audit log with the acting user and locked it read-only. Editors get a layout builder, draft and live preview and a maintenance mode.

Quality is guarded by Playwright e2e tests, Vitest integration tests and a single command that runs the design-token guard and the typecheck. Planning to move a website to a modern CMS without losing SEO? Get in touch.

HC Čestice — screenshot 1
HC Čestice — screenshot 2
HC Čestice — screenshot 3
HC Čestice — screenshot 4

Technology

Payload CMS 3 runs right inside Next.js 16, so the site and the admin share one project. Data lives in PostgreSQL through Drizzle, the interface is React 19, TypeScript, Tailwind and shadcn/ui. Playwright covers e2e tests and Vitest integration tests, content is written in Lexical and emails are sent through Resend. Deployment: Docker and GitHub Actions.

What I built

A full archive migration from the legacy site — an XML export parser and import pipeline for articles, sections, galleries and files, plus seed scripts for players, fixtures, AHL rosters, history milestones, sponsors and products.

URL preservation — a redirect generator mapping legacy paths onto the new pages, so the migration cost the club no inbound links or search rankings.

An adapter layer over an immature navigation plugin — it normalises the plugin's behaviour and throws on unexpected upstream changes. Without it a regression would surface only as an English admin UI, with no explanation.

A custom navigation model separating link label, anchor and path, with a resolver kept apart from the server-only CMS config — otherwise the CMS would leak into the client bundle and break the build.

Tag-based rather than time-based navigation caching, with revalidation hooks on both create and delete — without them the cache TTL is a full year; import scripts opt out.

An audit log extended with the document ID and the acting user — neither of which the plugin records — and locked read-only so an admin cannot delete their own trail.

Editorial features — layout builder, draft and live preview, on-demand revalidation, search, form builder, reCAPTCHA v3, content-type-split sitemaps and maintenance mode.

Quality — Playwright e2e tests, Vitest integration tests, a design-token guard and typecheck behind a single command, and an internal catalogue of UI patterns and widgets.

Let's talk about your project!