← All workCase study · Website

Barboursville Vineyards

A headless WordPress site for a winery in Virginia, with wines, events, tours, the restaurant and notes from the winemaker. Editors build pages in WordPress and choose their template.

Role
Sole frontend developer at PixelCrew
Timeline
Oct 2023 – 2024, maintenance to 2026
Team
PixelCrew
Live
bbvwine.com

Problem

Barboursville is a winery in Virginia. Its site ran on a classic WordPress theme. The rebuild keeps WordPress for the editors and moves the frontend to Next.js. Editors still write pages, events, wines and news in the same admin. The site covers the estate’s history, culture and sustainability, the wines, events by month, tours and tastings, the restaurant and the newsletter. The shop and the reservations link to external services.

Architecture

Next.js reads WordPress on the server, through custom REST endpoints with ACF fields and Yoast data. Pages are pre-rendered and refreshed every 45 seconds. Forms are sent to Gravity Forms. The sitemap is generated from the content in WordPress. Sentry tracks errors and GTM handles analytics.

The Next.js site reads WordPress REST on the server and refreshes static pages every 45 seconds. Forms go to Gravity Forms.Next.js sitecatch-all route · 18 templatesstatic pages, ISR 45 sWordPressREST · ACF · Yoastused by the editorsGravity Formscontact · newsletterevery 45 sform submitSitemap · metadatafrom CMS content and YoastSentry · GTMerrors · analyticsShop · bookingsexternal, linkedbuilt by meowned by others
Next.js reads WordPress on the server, through custom REST endpoints with ACF fields and Yoast data. Pages are pre-rendered and refreshed every 45 seconds. The sitemap is generated from the content in WordPress.

Scope

Mine

  • The whole frontend: the 18 templates, navigation, filters and forms
  • Routing driven by the content in WordPress
  • SEO metadata and the sitemap, generated from the CMS

Other teams

  • WordPress setup and content (PixelCrew and the client’s editors)
  • Shop and reservations (external services)

Key decisions

One catch-all route, with the template chosen in WordPress

Why. Editors create pages without touching the code. The route looks up the URL among the pages in WordPress, reads the type (page, post, event, wine) and the template the editor picked, and renders the matching component.

Trade-off. The routing lives in one file of about 400 lines.

Static pages, refreshed every 45 seconds

Why. The pages are served as static files, and content changes still go live within 45 seconds. No webhook or rebuild is needed.

Trade-off. A change can take up to 45 seconds to show up.

SEO managed in Yoast

Why. The editors already used Yoast. Its output becomes Next.js metadata and JSON-LD, so titles, descriptions and social previews stay in their hands.

Trade-off. The frontend depends on the format of Yoast’s output.

In code

The catch-all route, simplified:

app/[[...slug]]/page.tsxillustrative
export default async function Page({ params }: PageProps) {
  // Every published URL in WordPress, with its type
  const pages = await getRoutes();
  const current = pages.find((p) => samePath(p.slug, params.slug));
  if (!current) notFound();

  if (current.type === "event") return <EventTemplate pages={pages} />;
  if (current.type === "wine" || current.type === "wine-category") {
    return <WinesLayout pages={pages} slugs={params.slug} />;
  }

  const page = await getPage(params.slug.at(-1));

  // The template is chosen by the editor in WordPress
  switch (page.template) {
    case "controllers/page-heritage.php":
      return <HeritagePageTemplate data={page} />;
    case "controllers/page-restaurant.php":
      return <RestaurantPageTemplate data={page} />;
    // …16 more templates
  }
}

Results

18
page templates the editors choose from WordPress
6
content types managed in the CMS
45 s
for a content change to reach the live site, with no rebuild

The editors kept the admin they knew, and they still create pages without a developer. The site has 18 templates and 6 content types, all driven by WordPress. It has been live since 2024, and I maintained it until 2026.