Skip to content
Rafi Akmal Widikta

frontend

Incentro

Multi-locale corporate website for an IT consultancy, built on a headless CMS with its own search indexer.

Role
Software Engineer
Date
January 2026

Technologies

  • next.js
  • react
  • typescript
  • contentful
  • graphql
  • next-intl
  • algolia
  • fastify
  • redis
  • turborepo
  • tailwind css
  • gcp

Problem

The site is a marketing platform for offices in several regions: eleven locale variants, and every page driven by content editors rather than by code. That combination breaks the usual assumptions. A statically generated site is fast but goes stale; one rendered per request is fresh but pays for it on every hit. Search has the same problem in a different shape: new case studies and insights have to become findable quickly, without editors having to think about an index.

A platform this size is maintained more than it is built, and the maintenance had fallen behind. Dependency update pull requests had piled up unmerged for weeks; not because the updates themselves were risky, but because the lockfile had drifted out of agreement with the dependency declarations, so every one of them conflicted. The user-facing bugs waiting in the same backlog all had the same shape: they only show up at the edges. Times rendered for the Indonesian locale were wrong, because the date formatter fell back to a European timezone for every language. The hero on the campaign pages was sized with percentage margins that only held at one viewport, leaving a band of empty space underneath it. The text in the contact form was pushing its way out through the edge of its panel. And one process failure is worth naming: a merge landed on the wrong branch and carried 111 unrelated files with it, so features that were never meant to move read as regressions.

Solution

Content lives in Contentful and is fetched through GraphQL. The schema is generated from the CMS and the TypeScript types are inferred from it with gql.tada, so a field that changes in Contentful fails at build time instead of rendering blank. Pages are server components in the Next.js App Router, which keeps each fetch next to the component that needs it.

Two decisions did most of the work. Caching is layered, and the Next.js cache handler is backed by Redis, so every instance shares one cache and sees the same invalidation instead of each holding its own copy, which is what makes horizontal scaling safe rather than merely possible. Locale routing goes through next-intl with localized pathnames, which lets eleven locales share one codebase and still render statically. Next.js itself is patched with pnpm patch to fix an s-maxage bug on 404 responses.

Search runs as a separate service: a Fastify app that indexes Contentful into Algolia, triggered by a CMS webhook on publish and by a nightly scheduler for a full pass. Keeping it out of the web app means an indexing failure can never take the site down. The whole thing is a Turborepo monorepo (web app, indexer, and shared packages) deployed to Cloud Run as a standalone Next.js server, with Playwright running visual regression in CI, which matters when the layout is CMS-driven.

Sorting out the dependencies was a sequence rather than a single command: bring main up to date, merge it into each update branch one at a time, then combine the results on an aggregate branch and resolve the lockfile conflicts until it agreed with itself again. The stray merge had to be reverted first: the quickest way to unblock was to undo it and redo it in the right place.

Each bug was fixed at its cause rather than at its symptom, and the timezone one is the clearest example of arriving at the smaller answer. The first pass added a mapping of the three Indonesian zones plus a helper that parsed the offset out of the date string and normalised it. The second pass deleted all of it: for the locales that actually needed the correction, stripping the offset from the ISO string was enough. 82 lines became 12, with no change in behaviour. The hero was rebuilt on a new mask asset with fixed margins per breakpoint instead of percentage ones, which is what finally removed the leftover space. The form text was brought back inside the panel by clipping it and giving it responsive bottom padding, rather than forcing the scroll area to full height. And the navigation spec grew from two pages with stale assertions into a check of the logo, three menu items and the contact button across four pages.

Gallery

Screenshots

  • Incentro homepage with an orange hero panel reading “Digital solutions that set you apart” beside a photo collage.
    Homepage

Contact

Interested in working together?

Tell me what you are building and what you need. A short note is enough.

Back to top