Custom pages on ComStack
What this is
A custom page is a ComStack page whose entire body is your own code — a complete Astro component or HTML document, uploaded as one opaque body_source with the upload-custom-page tool. The platform never rewrites it: what you author is what ships (Astro’s standard build applies; plain-HTML bodies are round-tripped through a standards-compliant parser). Layout, styling, animation, and interactivity are entirely yours.
Custom pages exist for everything a structured markdown template can’t express: landing pages, product launches, interactive demos, calculators, animated showpieces, brand-heavy marketing pages. The rest of the site — docs, blogs, FAQs — belongs to knowledge pages and their templates; see Knowledge vs custom pages.
What is possible
Treat a custom page as a blank canvas with a fast CDN behind it:
- Total layout freedom — your own grids, sections, hero treatments, typography. No template structure is imposed on the body.
- Animation — CSS keyframes, scroll-driven effects, transitions, SVG animation. Inline
<style>and<script>tags ship as authored. - Interactivity — vanilla JS calculators, tabs, accordions, canvas effects, form-like widgets. No framework runtime is added or required.
- Automatic translation — wrap your visitor-facing strings in markers and every configured locale gets a native version of the page at publish, same URL structure, correct
hreflang. You write the page once. - Platform AEO for free — title/description metadata, JSON-LD, Open Graph, sitemap and
llms.txtinclusion are handled by the platform shell around your body.
A page generated in Lovable, v0, or Bolt — or written by hand, or by an AI agent in one shot — can be live in minutes: add markers, upload, publish.
How it works
- Author a complete Astro component or HTML document elsewhere (any tool, any agent).
- Mark translatable strings with
<T key="…" default="…">(Astro/JSX) ordata-t-key(HTML) — the upload extracts them intotranslatable_strings; publish translates them into every configured locale. Never hand-translate. See Translation. - Upload with
upload-custom-page(project_id,project_name,slug,body_source,title10–60 chars,description50–160 chars). The result is a draft — nothing is live yet. - Publish with the standard two-step flow:
publish(dry-run manifest + confirmation token) →publish-confirm→ pollpublish-status. See Publishing.
Re-uploading the same slug replaces the body in one call; the live URL stays up until you publish the new draft.
Hard boundaries
Three constraints are non-negotiable, by design:
- The body is opaque, but the envelope is not. Your code renders inside the platform shell that provides
<head>metadata, AEO tags, locale routing, and security headers. You author the<body>-level content, not the document head. - Dangerous href schemes are rejected at upload.
javascript:,data:andvbscript:URLs inhref/xlink:hrefattributes fail the upload outright — the body ships to the browser as authored, so the gate is at the door.data:URLs insrc(inline images) are fine. See Troubleshooting. - Quality is gated, not hoped for. Custom pages are audited against Lighthouse with a bar of 95 in performance, accessibility, best practices, and SEO at pre-publish. Build to that bar from the first line. See Quality gates.
When to use it
- A landing or campaign page that must look nothing like the rest of the site
- A product launch page with bespoke animation and scroll effects
- An interactive page — calculator, configurator, quiz — in plain HTML/JS
- Importing a vibe-coded export from Lovable, v0, or Bolt
- Any page where a markdown template is the wrong tool
For structured editorial content (docs, articles, FAQs), use a knowledge page instead — you get templates, validation, and FAQ JSON-LD that custom pages deliberately do not have.
Example
The smallest valid custom page body (HTML form):
<section class="hero"> <h1 data-t-key="hero.title">Fresh bread, every morning</h1> <p data-t-key="hero.sub">Family bakery in the old town since 1962.</p> <a class="cta" href="tel:+34600000000" data-t-key="hero.cta">Call the bakery</a></section><style> .hero { min-height: 60vh; display: grid; place-content: center; text-align: center; } .cta { display: inline-block; padding: .8rem 1.6rem; border-radius: 999px; background: #1a7f5a; color: #fff; }</style>Upload it, publish, and /es/ (plus every other configured locale) renders the same layout with translated strings substituted into the marker positions. Two fully annotated walkthroughs — minimal and advanced — are in Recipes.
Common errors
- Marker mistakes — unmarked strings stay untranslated; malformed markers reject the upload with character offsets. See Translation.
wrong_tool_knowledge_page— the slug already belongs to a markdown page. Pick another slug or edit that page withupdate-page.- Lighthouse failures — heavy images, render-blocking scripts, missing alt text. The fixes are mechanical; see Quality gates.
The full catalogue with exact messages and fixes: Troubleshooting.
Related
- Author a custom page — the contract and the loop, end to end
- Translation — markers,
translatable_strings,canonical_slug - Quality gates — Lighthouse, performance and AEO expectations
- Troubleshooting — every rejection and its fix
- Recipes — annotated minimal and advanced examples
- Example gallery — live pages built the way these docs teach