Skip to main content
This guide shows you how to build a brand website using Publive’s Decoupled Frontend Infrastructure. You’ll learn how to:
  • Set up the brand website starter
  • Create brand pages in Publive CMS
  • Build reusable layout sections
  • Register and render custom components
  • Deploy your changes to production

Prerequisites

Before you begin, ensure you have completed onboarding and received:
GitHub Repository Access - Clone access to your brand starter repo
Publive CMS Access - Login credentials to the CMS dashboard
Publisher Credentials - API keys and secrets for authentication
Environment Mapping - Branch-to-environment mapping (e.g., beta → staging, main → production)
Deployment URL - Live URL where your site will be hosted

Step 1: Run the Starter Locally

Clone the Repository

git clone git@github.com:ThePublive/pl-brand-starter.git
cd pl-brand-starter
Replace pl-brand-starter with your actual repository name provided during onboarding.

Install Dependencies

npm install

Configure Environment Variables

Copy the sample environment file:
cp .env.sample .env
Add your credentials to .env:
.env
NEXT_PUBLIC_PL_PUBLISHER_ID=your-publisher-id
NEXT_PUBLIC_PL_PUBLISHER_API_KEY=your-api-key
NEXT_PUBLIC_PL_PUBLISHER_API_SECRET=your-api-secret
NEXT_PUBLIC_PL_ENVIRONMENT=production
NEXT_PUBLIC_APP_URL=https://your-domain.com

Start Development Server

npm run dev
Open your browser to:
http://localhost:3000
You now have a working brand website powered by Publive CMS data!

Step 2: Understand the Brand Page Model

Brand pages in Publive are built from layout sections - reusable building blocks that you can arrange in any order.

Page Structure

Each brand page contains:
metadata
object
Page-level information like title, slug, and SEO fields
layout
array
Ordered list of section components that make up the page

Section Structure

Each layout section has:
schema_slug
string
required
Unique identifier for the section type (e.g., hero-banner-image)
fields
object
required
Structured content fields specific to that section type

Rendering Flow

The website automatically:
  1. Reads the layout array from CMS
  2. Finds the matching React component for each schema_slug
  3. Passes the section’s fields as props to the component
  4. Renders components in the specified order

Step 3: Create Your First Brand Page

In Publive CMS

1

Open Content Type

Navigate to your brand pages content type in the CMS
2

Verify Layout Field

Ensure the content type has a layout (dynamic zone) field
3

Create New Page

Click “Create New Entry” to start a new brand page
4

Add Metadata

Fill in basic information:
  • Title: Page title for SEO
  • Slug: URL path (e.g., about-us)
  • Meta Description: SEO description
5

Add Layout Sections

Click “Add Section” in the layout field and choose from available section types
6

Publish

Click “Publish” to make your page live
Your page will now render automatically on your website at the slug you specified!

Step 4: Add Sections to Your Page

Sections are reusable building blocks. Let’s create a hero banner section as an example.

Define Section in CMS

1

Create Section Schema

In CMS, create a new section schema with these fields:
schema_slug: hero-banner-image
fields:
  - name: heading
    type: text
    required: true
  - name: sub_text
    type: text
    required: false
  - name: description
    type: rich-text
    required: false
  - name: banner
    type: media
    required: false
2

Add to Page Layout

In your brand page, add this section to the layout field
3

Fill Content

Populate the section fields with your content:
  • Heading: “Welcome to Our Brand”
  • Sub text: “Building the future”
  • Upload a banner image

Step 5: Build the Matching Component

Create a React component that matches the CMS fields exactly.

Create Component File

components/sections/HeroBannerImage.tsx
import { Media } from "publive-cms-sdk";
import Image from "next/image";

interface HeroBannerImageProps {
  heading: string;
  sub_text?: string;
  description?: string;
  banner?: Media;
}

export default function HeroBannerImage({
  heading,
  sub_text,
  description,
  banner,
}: HeroBannerImageProps) {
  return (
    <section className="relative h-[600px] w-full">
      {/* Background Image */}
      {banner && (
        <div className="absolute inset-0">
          <Image
            src={banner.url}
            alt={banner.alt_text || heading}
            fill
            className="object-cover"
            priority
          />
          <div className="absolute inset-0 bg-black/40" />
        </div>
      )}

      {/* Content */}
      <div className="relative z-10 flex h-full items-center justify-center">
        <div className="max-w-4xl text-center text-white">
          <h1 className="mb-4 text-5xl font-bold">{heading}</h1>
          
          {sub_text && (
            <p className="mb-6 text-xl font-light">{sub_text}</p>
          )}
          
          {description && (
            <div 
              className="prose prose-invert"
              dangerouslySetInnerHTML={{ __html: description }}
            />
          )}
        </div>
      </div>
    </section>
  );
}
Critical Rule: Component prop names MUST match CMS field names exactly. If CMS uses sub_text, your component must use sub_text (not subText or subtitle).

Step 6: Register the Section

Map the section slug to your component in the configuration file.

Update Configuration

publive.config.ts
import HeroBannerImage from "@/components/sections/HeroBannerImage";

export const componentRegistry = {
  "hero-banner-image": {
    component: HeroBannerImage,
  },
  // Add more sections here...
};
Now, when CMS sends a layout item with:
{
  "schema_slug": "hero-banner-image",
  "heading": "Welcome to Our Brand",
  "banner": { ... }
}
The website automatically renders the HeroBannerImage component with those props.

Step 7: Create Custom Sections

Follow this workflow to build new sections from scratch:
1

Design Section

Create or review the design in Figma or design tool
2

Define CMS Schema

Create the section schema in CMS with a unique schema_slug
schema_slug: testimonial-carousel
fields:
  - name: title
    type: text
  - name: testimonials
    type: repeater
    fields:
      - name: quote
        type: text
      - name: author
        type: text
      - name: avatar
        type: media
3

Build React Component

Create a component matching the CMS fields
interface TestimonialCarouselProps {
  title: string;
  testimonials: Array<{
    quote: string;
    author: string;
    avatar?: Media;
  }>;
}
4

Register Component

Add to publive.config.ts:
"testimonial-carousel": {
  component: TestimonialCarousel,
}
5

Add to CMS Page

Add the section to a page’s layout field in CMS
6

Test & Verify

Check the section renders correctly with all field variations
This loop is how brand websites scale. Each new section becomes a reusable building block.

Step 8: Validate Your Page

Before deploying, verify:
✅ Sections render in the same order as the CMS layout array
✅ Optional fields don’t break rendering when empty
{sub_text && <p>{sub_text}</p>}
✅ Images load correctly with proper alt text ✅ Image optimization is working (Next.js Image component)
✅ Draft content only shows in preview mode ✅ Published content appears on live site

Step 9: Deploy Your Changes

Standard Deployment Workflow

1

Create Feature Branch

git checkout -b feature/add-testimonial-section
2

Commit Changes

git add .
git commit -m "feat: add testimonial carousel section"
3

Push to Remote

git push -u origin feature/add-testimonial-section
4

Create Pull Request

Open a PR on GitHub to your environment branch (e.g., beta or main)
5

Review & Merge

After code review, merge the PR
6

Automatic Deployment

CI/CD automatically deploys the latest version to your live URL

Branch → Environment Mapping

Confirm your branch-to-environment mapping during onboarding:
beta Staging Environment (staging.yourdomain.com)
main Production Environment (yourdomain.com)
Merging to main triggers immediate production deployment. Always test in staging first!

Common Section Patterns

Text Content Section

interface TextContentProps {
  title: string;
  content: string; // Rich text HTML
}
interface ImageGalleryProps {
  title?: string;
  images: Media[];
  layout: "grid" | "carousel";
}

Call-to-Action Section

interface CTASectionProps {
  heading: string;
  description?: string;
  primary_button: {
    text: string;
    url: string;
  };
  secondary_button?: {
    text: string;
    url: string;
  };
}

Team Section

interface TeamSectionProps {
  title: string;
  team_members: Array<{
    name: string;
    role: string;
    bio?: string;
    photo?: Media;
    linkedin?: string;
  }>;
}

Best Practices

Type Safety

Define TypeScript interfaces for all section props

Prop Naming

Match CMS field names exactly in component props

Optional Fields

Handle optional fields gracefully with conditional rendering

Image Optimization

Always use Next.js Image component for automatic optimization

Reusability

Design sections to be reusable across multiple pages

Testing

Test all field variations (required, optional, empty states)

Next Steps

Component Library

Explore pre-built section components

Styling Guide

Learn styling best practices for sections

SEO Optimization

Optimize your brand pages for search engines

Performance

Performance optimization techniques

Troubleshooting

Possible causes:
  • schema_slug mismatch between CMS and config
  • Component not exported correctly
  • Prop name mismatch between CMS fields and component
Solution: Check browser console for errors and verify naming matches exactly.
Possible causes:
  • Image URL not accessible
  • Missing Next.js Image domain configuration
Solution: Add CMS domain to next.config.js:
images: {
  domains: ['cdn.publive.com'],
}
Possible causes:
  • CSS modules not imported
  • Tailwind classes not generated
Solution: Ensure CSS is imported and Tailwind config includes your component paths.

Support

Need help building your brand website?