nlynx.digital
TemplatesBlogIntegrationsAbout
Browse Templates
nlynx.digital

High-Performance Next.js Templates. Production-ready Next.js templates with Lighthouse 95+ scores.

Templates

  • Skincare E-Commerce
  • Agency Branding Page
  • View all templates →

Resources

  • Blog
  • Integrations
  • About

Legal

  • Privacy Policy
  • Terms of Service

© 2026 nlynx.digital. All rights reserved.

Built with Next.js & Tailwind CSS

Back to Blog
nextjscmswordpressshopifysanityarchitecture

CMS-Agnostic Next.js Architecture: Connect WordPress, Shopify, or Sanity

May 17, 20267 min readBy nlynx.digital

Learn how to build Next.js websites that work standalone and can connect to any CMS. The hybrid architecture pattern that gives you maximum flexibility.

The CMS Problem

Every Next.js template project eventually hits the same question: which CMS should we use? Choose Sanity and you are locked into its data model and pricing. Choose WordPress and you inherit its complexity. Choose Shopify and e-commerce is baked in but content management is limited.

The CMS-agnostic architecture solves this by decoupling your frontend from any specific content backend. Your template works perfectly as a static site with no CMS at all — and you can wire in any CMS later without rewriting your components.

Why CMS-Agnostic Matters

In a traditional CMS-coupled template, the data fetching is baked into the components:

// Tightly coupled — cannot swap CMS without rewriting components
const products = await sanityClient.fetch('*[_type == "product"]')

This creates an invisible dependency. The moment you choose Sanity, every component that fetches data knows about Sanity. Switching to Shopify means rewriting every component.

With a CMS-agnostic architecture, your components never know where the data comes from:

// Decoupled — component works with any data source
const products = await getProducts()

The `getProducts` function is defined in a single data adapter file. Swapping CMS means changing one import in one file, not rewriting 50 components.

The Data Adapter Pattern

The pattern has three layers:

Layer 1: Your components consume typed data via functions. They have no knowledge of where the data originates.

Layer 2: The data adapter (`src/lib/data-adapter.ts`) re-exports the active adapter's functions:

// Swap this one line to change your entire data source
export { getProducts, getProduct, getBlogPosts } from './adapters/static'
// export { getProducts, getProduct } from './adapters/shopify'
// export { getBlogPosts } from './adapters/sanity'

Layer 3: The adapters implement the data fetching logic for each CMS. Each adapter exports the same function signatures, so they are interchangeable.

How to Connect WordPress Headless

WordPress with the REST API is the most common "I already have content in WordPress" scenario. The adapter fetches from the REST API endpoint:

// src/lib/adapters/wordpress.ts
const WP_API = process.env.WORDPRESS_API_URL

export async function getBlogPosts(): Promise<BlogPost[]> {
  const res = await fetch(`${WP_API}/wp/v2/posts?_embed`, {
    next: { revalidate: 3600 }, // ISR: revalidate every hour
  })
  const posts = await res.json()
  return posts.map(transformWordPressPost)
}

The `transformWordPressPost` function maps WordPress's field names to your TypeScript types. Your components never see the WordPress response structure — they always receive your typed `BlogPost` objects.

How to Connect Shopify Storefront API

For e-commerce templates, the Shopify Storefront API provides product data via GraphQL:

// src/lib/adapters/shopify.ts
export async function getProducts(): Promise<Product[]> {
  const response = await fetch(
    `https://${process.env.SHOPIFY_STORE_DOMAIN}/api/2024-01/graphql.json`,
    {
      method: 'POST',
      headers: {
        'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_ACCESS_TOKEN!,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ query: PRODUCTS_QUERY }),
    }
  )
  const { data } = await response.json()
  return data.products.edges.map(({ node }) => transformShopifyProduct(node))
}

The template starts with static product data. When the client is ready to connect their Shopify store, you change one line in `data-adapter.ts` and add two environment variables.

How to Connect Sanity CMS

Sanity uses GROQ (its own query language) via the `@sanity/client` package:

// src/lib/adapters/sanity.ts
import { createClient } from '@sanity/client'

const client = createClient({
  projectId: process.env.SANITY_PROJECT_ID!,
  dataset: process.env.SANITY_DATASET!,
  apiVersion: '2024-01-01',
  useCdn: true,
})

export async function getBlogPosts(): Promise<BlogPost[]> {
  const posts = await client.fetch('*[_type == "post"] | order(publishedAt desc)')
  return posts.map(transformSanityPost)
}

Sanity is ideal for content-heavy sites where non-technical editors need a visual editing interface. The real-time preview feature in Sanity Studio is unmatched for marketing teams.

Static JSON: The Default That Works for Everyone

The default adapter reads from JSON files in `src/data/`. This means:

- Zero cost — no CMS subscription

- Zero setup — no accounts, no API keys, no environment variables

- Version controlled — content changes are tracked in git

- Instant — reading from the filesystem is faster than any API call

For most template buyers, static JSON is all they ever need. The CMS adapters are there when content volume grows beyond what comfortable to manage in files.

Making the Switch: Zero Friction

The beauty of this architecture is how effortlessly you can upgrade. A client starts with static JSON. Six months later, their team wants a visual editor. You:

1. Add `@sanity/client` to dependencies

2. Create `src/lib/adapters/sanity.ts`

3. Change one import in `src/lib/data-adapter.ts`

4. Add two environment variables

No component rewrites. No data model changes. No downtime.

Conclusion

CMS-agnostic architecture is not complexity for its own sake — it is a deliberate design choice that gives you and your clients maximum flexibility with minimum lock-in. Every template on nlynx.digital implements this pattern. You get a site that works on day one with zero dependencies, and the freedom to connect any CMS when you need to scale.

More articles