A demonstration project showcasing Next.js static site integration with LeadCMS headless CMS.
This portfolio site for CTO Peter L. serves as a practical example of how to:
- ✅ Build a static Next.js site powered by LeadCMS
- ✅ Separate content (MDX/JSON) from presentation (React components)
- ✅ Use MDX components for rich, editable content
- ✅ Deploy as a fully static site with zero runtime dependencies
- ✅ Set up CI/CD with GitHub Actions and Docker
The site follows the LeadCMS architecture pattern:
- Content lives in
.leadcms/content/as MDX and JSON files - Presentation is handled by React components in
components/ - Templates in
components/templates/control how different content types are rendered - No fallback content - the build fails if required content is missing
.leadcms/content/
├── metadata.json # Site-wide metadata
├── header.json # Header navigation configuration
├── footer.json # Footer configuration
├── contact-us-form.json # Contact form configuration
├── home.mdx # Homepage content
└── projects/ # Project pages
├── xltools.mdx
├── leadcms.mdx
└── tagpoint.mdx
The following components are available in MDX files:
<HeroSection>- Hero section with profile image and bio<ProjectCard>- Individual project card<ProjectGrid>- Grid container for project cards<ContactSection>- Contact form section
- home - Homepage (uses DefaultTemplate)
- project - Project detail pages (uses ProjectTemplate)
pnpm run dev- Start development serverpnpm run build- Build for production (includes content fetch)pnpm run pull- Fetch latest content from LeadCMSpnpm run start- Start production server
pnpm run docker:build- Build static production Docker imagepnpm run docker:run- Run static production Docker imagepnpm run docker:preview:build- Build preview development Docker imagepnpm run docker:preview:run- Run preview development Docker image
Required environment variables in .env:
LEADCMS_API_KEY=your-api-key
LEADCMS_URL=https://your-cms-name.leadcms.ai
LEADCMS_DEFAULT_LANGUAGE=enNote: Replace https://your-cms-name.leadcms.ai with your actual LeadCMS instance URL.
The site is configured for static export in production:
- All pages are pre-rendered at build time
- Dynamic routes use
generateStaticParams - Images are unoptimized for static deployment
- Create an MDX file in
.leadcms/content/ - Add frontmatter with
title,description,slug, andtype - Use existing MDX components to compose the page
- The page will automatically be generated
- Create component in
components/mdx/ - Export from
components/mdx/index.tsx - Register in
components/mdx-components.tsx - Use in MDX files
- Create template in
components/templates/ - Register in
components/templates/index.tsx - Use by setting
typein content frontmatter
- No hardcoded content - All content comes from
.leadcms/content/ - Content inside tags - MDX components accept content as children, not JSON props
- Fail builds - Missing content throws errors instead of showing fallbacks
- Semantic names - Components have clear, descriptive names
- Static export compatible - No server-side features, all generated at build time
Start the development server:
pnpm run devVisit http://localhost:3000 to see the site.
Build the site for production:
pnpm run buildThis will:
- Fetch latest content from LeadCMS
- Generate all static pages
- Export to the
out/directory
The project provides two Docker images for different use cases:
Pre-built static site served by nginx - ready for production deployment.
# Pull from Docker Hub
docker pull leadcms/nextjs-cto-portfolio-static:latest
# Run the container (with LeadCMS URL for contact form)
docker run -p 80:80 \
-e NEXT_PUBLIC_LEADCMS_URL=https://your-cms-name.leadcms.ai \
leadcms/nextjs-cto-portfolio-static:latestFeatures:
- ✅ Fully static HTML/CSS/JS (no server-side runtime)
- ✅ Runtime environment injection for client-side API calls
- ✅ Optimized nginx configuration
- ✅ Small image size (~50MB)
- ✅ Fast startup time
- ✅ Production-ready
Note: The NEXT_PUBLIC_LEADCMS_URL environment variable is injected at container startup and made available to client-side JavaScript for features like the contact form.
Full development environment with live preview and content watching.
# Pull from Docker Hub
docker pull leadcms/nextjs-cto-portfolio-preview:latest
# Run with LeadCMS connection (API key required)
docker run -p 80:80 \
-e LEADCMS_URL=https://your-cms-name.leadcms.ai \
-e LEADCMS_API_KEY=your-api-key \
leadcms/nextjs-cto-portfolio-preview:latest
# With custom language
docker run -p 80:80 \
-e LEADCMS_URL=https://your-cms-name.leadcms.ai \
-e LEADCMS_API_KEY=your-api-key \
-e LEADCMS_DEFAULT_LANGUAGE=en \
leadcms/nextjs-cto-portfolio-preview:latestFeatures:
- ✅ Next.js dev server with hot reload
- ✅ LeadCMS content watcher (live updates via SSE)
- ✅ Nginx reverse proxy
- ✅ Supervisor for multi-service management
- ✅ Ideal for preview environments
Required Environment Variables:
LEADCMS_URL- LeadCMS instance URLLEADCMS_API_KEY- API key for live watch mode (required for preview container)
Optional Environment Variables:
LEADCMS_DEFAULT_LANGUAGE- Default language (defaults toen)
Deploy the out/ directory to any static hosting service:
- Vercel (recommended for Next.js)
- Netlify
- Cloudflare Pages
- AWS S3 + CloudFront
- GitHub Pages
Use the static Docker image for containerized deployments:
# Production deployment
docker run -d -p 80:80 --name portfolio \
--restart unless-stopped \
-e NEXT_PUBLIC_LEADCMS_URL=https://your-cms-name.leadcms.ai \
leadcms/nextjs-cto-portfolio-static:latestEnvironment Variables:
NEXT_PUBLIC_LEADCMS_URL- Required for client-side features (contact form submissions)
The project includes automated GitHub Actions workflow (.github/workflows/build-and-push.yml) that:
- ✅ Builds static site on every commit to
main/develop - ✅ Uploads build artifacts (downloadable from Actions tab)
- ✅ Builds and pushes Docker images to Docker Hub
- ✅ Tags images by branch and commit SHA
Note: The pipeline builds and pushes artifacts but does not automatically deploy them. You'll need to set up deployment separately based on your hosting choice.
This project demonstrates:
- LeadCMS Integration - Content managed in LeadCMS, synced via SDK
- Static Generation - Full static export with
output: "export" - MDX Components - Rich content composition with custom React components
- Template System - Different layouts for different content types
- CI/CD Pipeline - Automated builds and Docker images with GitHub Actions
- Best Practices - No hardcoded content, fail-fast builds, semantic components
Live Site: https://liapin.space (Portfolio of CTO Peter L.)
This demo project is open source and available for learning purposes.