Landing Pages (Page Builder)¶
The Landing Pages feature provides a complete page building system with WYSIWYG editing, custom blocks, MkDocs export, and public rendering. Build custom landing pages without code.
Overview¶
The Landing Pages system consists of four integrated components:
- Page Builder - Page CRUD and management
- GrapesJS Editor - WYSIWYG editor
- Block Library - Reusable content blocks
- MkDocs Export - Export to Jinja2 templates
Features¶
WYSIWYG Editor¶
- GrapesJS integration
- Drag-and-drop interface
- Visual editing
- Component customization
- CSS styling
- Responsive preview
- Desktop-only (mobile warning)
Block Library¶
Pre-built components:
- Hero sections - Large header with CTA
- Feature grids - Multi-column features
- Call-to-action - Button sections
- Text blocks - Rich text content
- Image galleries - Photo grids
- Forms - Contact forms (future)
- Custom HTML - Raw HTML blocks
Page Management¶
- Create/edit/delete pages
- Slug management (URL-friendly)
- Meta tags (title, description)
- Published/draft status
- Page settings (layout, scripts)
- Search and filtering
MkDocs Export¶
- Export to Jinja2 Material theme templates
- Custom overrides directory
- Static page generation
- SEO optimization
- Template inheritance
User Flow¶
Admin Experience¶
- Create Page (
/app/pages) - Click "New Page"
- Enter title and slug
- Set meta description
-
Save draft
-
Edit Page (
/app/pages/:id/edit) - Full-screen GrapesJS editor
- Drag blocks from sidebar
- Customize components
- Ctrl+S to save
-
Preview changes
-
Publish Page
- Set status to "Published"
- Page appears at
/p/:slug -
Listed in page table
-
Export to MkDocs (
/app/services/docs) - Select pages to export
- Click "Export"
- Pages saved to MkDocs overrides
- Rebuild MkDocs site
Public Experience¶
- View Landing Page (
/p/:slug) - Rendered HTML/CSS
- Custom styling
- Responsive design
- SEO metadata
Architecture¶
Backend Components¶
Module:
- api/src/modules/pages/pages-admin.routes.ts - Admin CRUD
- api/src/modules/pages/pages-public.routes.ts - Public renderer
- api/src/modules/pages/blocks.routes.ts - Block library API
- api/src/modules/pages/pages.service.ts - Business logic
- api/src/modules/pages/pages.schemas.ts - Zod validation
Database Models:
- Page - Page definitions (title, slug, html, css, settings)
- PageBlock - Reusable block library
Frontend Components¶
Admin Pages:
- admin/src/pages/LandingPagesPage.tsx - Page management table
- admin/src/pages/PageEditorPage.tsx - Full-screen editor
Public Pages:
- admin/src/pages/public/LandingPage.tsx - Page renderer
Editor Component:
- admin/src/components/GrapesJSEditor.tsx - GrapesJS wrapper
Configuration¶
Environment Variables¶
Page Settings¶
Each page can configure:
- Meta title - Browser title tag
- Meta description - SEO description
- Custom CSS - Page-specific styles
- Custom JS - Page-specific scripts
- Layout - Template wrapper (future)
GrapesJS Integration¶
Editor Setup¶
import grapesjs from 'grapesjs';
const editor = grapesjs.init({
container: '#gjs',
fromElement: true,
height: '100vh',
storageManager: false, // Save via API
canvas: {
styles: [...], // Custom styles
scripts: [...], // Custom scripts
},
blockManager: {
blocks: customBlocks, // Block library
},
});
Custom Blocks¶
Blocks defined in GrapesJSEditor.tsx:
const customBlocks = [
{
id: 'hero-section',
label: 'Hero Section',
content: '<div class="hero">...</div>',
category: 'Basic',
},
{
id: 'feature-grid',
label: 'Feature Grid',
content: '<div class="features">...</div>',
category: 'Content',
},
// ... more blocks
];
Save Handler¶
Ctrl+S keyboard shortcut:
editor.on('run:core:save', () => {
const html = editor.getHtml();
const css = editor.getCss();
onSave({ html, css });
});
MkDocs Export¶
Export Process¶
- Select Pages - Admin selects pages to export
- Generate Jinja2 - Wrap HTML in Material theme template
- Save to Overrides - Write to
mkdocs/docs/overrides/ - Configure Front Matter - Set template, title, description
- Rebuild Site - MkDocs regenerates static site
Jinja2 Template Wrapper¶
{% extends "main.html" %}
{% block content %}
<style>
{{ page_css }}
</style>
{{ page_html }}
{% endblock %}
Front Matter¶
Database Schema¶
Page Model¶
model Page {
id Int @id @default(autoincrement())
title String
slug String @unique
html String @db.Text
css String? @db.Text
settings Json?
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
PageBlock Model¶
model PageBlock {
id Int @id @default(autoincrement())
name String
category String
html String @db.Text
css String? @db.Text
thumbnail String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
API Endpoints¶
Admin Endpoints¶
GET /api/pages # List pages
POST /api/pages # Create page
GET /api/pages/:id # Get page
PATCH /api/pages/:id # Update page
DELETE /api/pages/:id # Delete page
POST /api/pages/export-mkdocs # Export to MkDocs
GET /api/pages/blocks # Get block library
POST /api/pages/blocks # Create block
Public Endpoints¶
Desktop-Only Editor¶
GrapesJS editor requires desktop browser:
const screens = Grid.useBreakpoint();
const isMobile = !screens.md;
if (isMobile) {
return (
<Alert
message="Desktop Required"
description="Page editor requires desktop browser"
type="warning"
/>
);
}
Best Practices¶
Slug Management¶
- Auto-generate from title
- URL-friendly (lowercase, hyphens)
- Unique constraint
- Update URL on slug change
SEO Optimization¶
- Meta title (50-60 chars)
- Meta description (150-160 chars)
- Semantic HTML structure
- Alt text for images
- Heading hierarchy
Performance¶
- Minify CSS
- Lazy load images
- Async scripts
- Cache rendered pages
Responsive Design¶
- Mobile-first CSS
- Flexible grids
- Responsive images
- Touch-friendly buttons