The Client
A brand that prints custom labels, such as vinyl and paper stickers, for businesses in Indonesia. Its website has to sell: show the products, answer the price question straight away, and turn a visitor into a WhatsApp conversation with the sales team. It also runs a blog that brings in search traffic.
The Problem
The old site was static, with the articles on WordPress, and it was hard to change:
- Product pages and prices were part of the site itself, so a new product or a price update needed a developer.
- The design came from a Figma file that changed often, and keeping exported assets in sync was slow.
- The articles already ranked in search, so a new site could not change their URLs.
- The marketing team wanted to keep WordPress as their editor, without running a full public WordPress site that has to be patched and protected.
What I Did
I built a new site in which Next.js renders every page and WordPress only manages content. Both live in one repository and deploy from it.
- WordPress under /cms Products, articles, comments, subscribers
- An editor publishes, and WordPress calls the revalidate webhook
- Next.js Rebuilds only the pages that changed
- Visitors get cached pages, refreshed within seconds
WordPress as a private CMS
WordPress runs under /cms and serves only the admin and its REST API. A small plugin of mine adds what the site needs: a product post type with its own edit form, endpoints for products and previews, a newsletter subscriber list and the webhook that refreshes the site. Third-party plugins are pinned to known versions and never update on their own.
Pages that are fast and stay fresh
Pages are prerendered and cached. They refresh every five minutes, and at once when an editor saves a product or an article, so new content needs no deploy. Articles kept the same URLs as on the old site, and the sitemap and robots.txt are generated from the same data.
Products editors can manage
Product pages read their copy, images and SEO fields from WordPress. Adding a product in the admin adds it to the page, the header menu, the footer and the sitemap.
Prices from the sales team’s spreadsheet
The price calculator reads a table generated from the spreadsheet the sales team already maintains, with a toggle to show prices with or without VAT. The sales team changes prices in one place and the site follows.
Draft previews without a login on the site
The Preview button in WordPress checks that the editor may edit the post, then sends them to a link signed for one hour. The site shows the draft in the real design, including unsaved changes, and never caches or indexes it. The signature is the only credential, so the website holds no WordPress account.
Comments, likes and the newsletter
Visitors’ comments, likes, views and newsletter sign-ups go straight to WordPress, and NGINX takes the visitor’s IP from Cloudflare, so WordPress rate-limits real visitors rather than Cloudflare.
Releases with a way back
The site builds on my machine after linting and tests, uploads as a new release and switches over in seconds. The previous release stays on the server for a one-command rollback. The WordPress plugin deploys from the same repository after a PHP syntax check on the server. A staging domain behind Cloudflare carries noindex until the site goes live, and interactive diagrams of the data flow, routes and deploy live in the project itself.
The Stack
| Layer | Tool | Role |
|---|---|---|
| Frontend | Next.js, React, TypeScript | Renders every page, with incremental static regeneration |
| CMS | WordPress, custom plugin | Products, articles, comments and subscribers via REST |
| Hosting | RunCloud, NGINX, Supervisor | One server for the site and the CMS, release-based deploys |
| Edge | Cloudflare | DNS, TLS and the visitor IP for rate limits |
| Tracking | Google Tag Manager | Loaded only in production |
| Quality | Vitest, ESLint, Prettier | Unit tests for pricing, products, formatting and images |
The Result
The site is complete on staging and about to go live.
| Before | After |
|---|---|
| Products and prices changed by a developer | Products in WordPress, prices from the sales spreadsheet |
| Product changes needed a new build of the site | Published content shows on the site within seconds |
| Editors needed a public WordPress site | WordPress private under /cms, the public site in Next.js |
| Old article URLs at risk in a rebuild | Every article on its old URL |
The technical side of this setup, including where it costs more than a classic WordPress site, is in my note on a headless CMS for a brand site.
If your website needs to be fast for visitors and easy for editors, get in touch.