Developers Site building

Developers

Site building

How to build Frontpage Astro sites correctly via the CLI: pages registry, tokens, SEO, modules, and craft defaults.

Frontpage sites are Astro projects. Coding agents editing via pull/push should follow the same product contract as the in-editor agent. Without inventing backends or editing platform-owned files. Deep command examples live under Content & settings and File sync. In the CLI package: frontpage help site-building and AGENTS.md.

Starting on Free

Owners can create up to three active Free sites and start building without a card. Rebuild onboarding selects up to 15 pages using the current page priority order. Free includes 15 selected pages, Starter 30, Growth 50, and Scale 80. Select 15 or fewer pages to stay on Free before checkout. Rebuild scanning needs a publicly readable source website.

These are onboarding build selections, not a cap on existing site pages. Existing paid subscriptions stay unchanged when they cover the build. Usage limits, custom-domain publishing, and CLI access keep their current plan requirements. A Free first build does not unlock paid features. The CLI edits an existing site; it does not create an onboarding account or select a rebuild plan.

Stack

  • Astro 6 + TypeScript + Tailwind CSS v4
  • Pages under src/pages/ (home is src/pages/index.astro)
  • Every page wraps content in <BaseLayout title="…" description="…">
  • Design tokens: src/config/tokens.ts
  • Site registry (title, nav, pages map, optional seo): src/config/site.ts
  • Components under src/components/; static assets under public/

Page registry (site.ts)

The pages map is the path registry (which URL paths exist), not the authority for the browser tab title. Real title/description come from each page’s <BaseLayout> props.

  • Add a page: create src/pages/… and add a matching pages entry (and nav if it should appear in navigation) in the same change set before push.
  • Delete / rename: update or remove the file, pages entry, and any nav item together. Then set a real HTTP redirect with frontpage redirects set --from /old --to /new so bookmarks and search keep working.
  • Path mapping: index.astro → /, about.astro → /about, blog/index.astro → /blog.

One file per URL

For sets of similar pages (services, locations, guides, case studies), prefer one discrete .astro file per URL plus a shared layout component. Do not default to getStaticPaths() over a local src/data/*.ts array. Those pages are hard to edit individually in the product. Exception: Store product pages — one src/pages/products/item.astro (or shop) with data-fp-store-product, not one file per item. Blog/news/essay content belongs in the Articles module (frontpage articles), not a hand-rolled data-driven template.

Hard product fences

  • No backends. No API routes, serverless functions, or databases.
  • No fake forms or checkout. Use real modules (data-fp-* markup + frontpage modules) or Frontpage Store (Store) — never a fake cart or card form. Per-key CLI usage: Modules (frontpage modules <key>).
  • Astro only. Do not migrate the site to another framework.
  • Quoted attributes vs frontmatter. In frontmatter JavaScript, a backslash-quote is an escape. In an Astro attribute it is not. The quote after href= closes the string and the next token fails the compile. Put HTML in a frontmatter const and pass it as an expression (auditBody={auditBody}), or use an expression attribute. Never put an HTML tag or a backslash-quote inside a quoted JSX attribute.

Platform-owned vs customer files

Sync only src/, public/, content/. Platform overlay / scaffold paths are rejected (examples: BaseLayout.astro, module/extension shells, package.json, astro.config.mjs). Customer layouts such as DocsLayout.astro or GuideLayout.astro are fine when they are site-owned.

Shared chrome

Prefer src/components/Header.astro and src/components/Footer.astro imported on every page (including article landings). Alternatively set chrome: { managed: true } in site.ts for config-driven chrome from nav. Opt out per page with <BaseLayout chrome={false}>.

Design tokens

tokens.ts is a typed contract. Keep required groups/keys: colors (primary, secondary, background, text, muted, border), fonts (sans, serif, mono; optional display), spacing (xs–xl), radii (base, full). Optional article group for article-only overrides. Prefer surgical edits over rewriting the whole file; do not rename keys or redefine the type. Style pages with CSS variables / token-backed Tailwind (e.g. text-[color:var(--color-muted)]), not hardcoded brand colors.

Fonts: browse the installed catalog with frontpage fonts [--slot serif] [--query elegant] and copy the exact value string. Preferred path: set a brand role with frontpage tokens set --group fonts --key sans --value "…" (same CAS / active-run rules as push), then apply font-sans / font-serif / font-mono / font-display (or var(--font-*)) on elements. The build bundles catalog faces when the family string appears in tokens.ts and/or in a real CSS font-family / fontFamily / font-['…'] context under site source. Not from prose alone. Inline catalog accents can load after rebuild; off-catalog families silently fall back to system fonts.

SEO, favicon, 404

  • Site-wide defaults live in the seo block of site.ts (favicon, ogImage, siteName, canonicalBase when on a custom domain). Per-page overrides use <BaseLayout> props.
  • Prefer frontpage seo get|set for metadata tweaks without a full file rewrite. See Content.
  • Customize src/pages/404.astro if needed; keep <meta name="frontpage:404" content="true" />. Never add /404 to nav or pages.
  • robots.txt / sitemap.xml are platform-managed. Do not invent a Sitemap line. Use noindex on BaseLayout / seo set to keep pages out of search.

Redirects (301 / 308)

A permanent URL move is a row in the site redirect table, not a page and not a file. The hop is an HTTP 301 or 308 on the live host. Instant, no push or compile. frontpage revert does not undo redirects; frontpage redirects rm does.

frontpage redirects list
frontpage redirects set --from /old --to /new
frontpage redirects set --from /agents --to /agent --status 308
frontpage redirects rm --from /old
  • Same-origin pathnames only (/old → /new). No host, //, query, or hash.
  • Do not ship Astro.redirect, location.replace, or a meta refresh for a permanent move. Those are 200s.
  • Never add /404 or a redirect from to nav / pages.
  • If a page file still exists at from, delete it in the same change so visitors hit the hop.

Deep command examples: Content & settings.

People CRM

People CRM is a platform capability, not a data-fp-* module. Use frontpage people list|get|add|set|note|rm-note|follow-up|hide|unhide. Inbox stays the raw submission ledger. Hide is not a legal delete. Full logic, identity rules, status machine, and v1 map: People CRM.

Routines

A routine is an alarm, not a module. It enqueues the customer's prompt on a schedule. Several hours and several days stay on one row. frontpage routines add --cadence weekly --weekdays tue,thu,fri --hours 8am,12pm,4pm --timezone America/Chicago stores hours [8, 12, 16] and weekdays [2, 4, 5]. Ask for an IANA timezone. Do not guess UTC. Full guide: Routines.

Store

Frontpage Store holds the catalog. The page is yours. Use frontpage store items|variants|categories|settings|orders|payments|connect|status|disconnect for products, stock, store-wide checkout, Service fee, fulfillment, and Stripe. Integer cents only ($49.99 is 4999). Stock --add / --remove are signed delta. Write a data-fp-store-grid + data-fp-product-card and a data-fp-cart + data-fp-cart-line on the page (every store control carries data-fp-requires="store"). Frontpage fills from the Store catalog. The cart aside starts hidden. A product page is one src/pages/products/item.astro (or shop) with data-fp-store-product, filled from the live Store item, unless they said they do not want product pages. Product description is a small markdown subset. First paragraph is the card; the product-page bind is <div data-fp-store-bind="description">. Do not use a leading underscore. Do not write one typed file per product. Do not invent getShopItems on the site. Grid href/url default to /products/{itemId}. Store is cart-only. Standalone Pay now uses a saved Payments Offering and its bound markup; see Offerings. Saved Offering questions and customer-chosen amounts use the independent Payments drawer, without Store cart markup. Do not write a pay modal or /__shop/choose. Stripe connect is frontpage payments connect. Full guide: Store.

Modules & articles (summary)

Real lead forms use <form data-fp-form="slug"> with named inputs. No custom submit JS, honeypot, or action. Do not put a CRM webhook or token in markup; HubSpot / Pipedrive / Privyr copies are a site-level destination, then a per-form attach. See Forms destinations. Do not fake card fields or checkout. Manage lifecycle with frontpage modules / frontpage articles. To migrate a folder of pages into Articles, prefer frontpage prompt "migrate … into the articles module" rather than a fragile mass delete/write. Google reviews use data-fp-google-reviews / data-fp-google-ask with data-fp-requires="google_reviews". Never fake quote cards or a guessed Maps href; see Content & settings.

Craft defaults

  • Common sections (hero, features grid, pricing, testimonials, FAQ with <details>, CTA band, footer) are good defaults. Always styled from the site’s tokens, not a stock template look.
  • When asked for real copy, do not leave lorem/TODO placeholders. For large pages: push a complete skeleton first, then grow section by section across edits/pushes.
  • Media: frontpage media upload for a file you already have (images/PDFs ≤25 MB, video MP4/WebM/MOV ≤50 MB, SVG/text ≤4 MB proxy). frontpage images generate --prompt "…" [--count N] invents 1–4 AI variations of one picture (counts against AI $ and storage). Reference the returned sitePath (/__media/uploads/<slug>/…) in markup. Current editor and CLI video uploads attempt browser-compatible H.264 MP4 preparation before returning the attachment. Successful preparation returns the MP4’s path, filename and size. If conversion fails, the original is returned with a compatibility warning; its file extension alone does not guarantee playback. Never raw *.blob.vercel-storage.com URLs. Optional raster sizing: append ?w=320|640|960|1280 on /__media/… image paths only (not video).

Video upload results

Upload a local video with the current CLI. The same preparation applies to frontpage prompt --attach ./brand-video.mov. Existing uploaded URLs and videos already published on a page stay unchanged.

frontpage media upload ./brand-video.mov --site my-site --json

On conversion success, the result includes prepared: true, contentType: "video/mp4", the actual filename and sizeBytes, and the prepared sitePath. Embed that returned path:

<video controls playsinline preload="metadata" src="RETURNED_SITE_PATH"></video>

If preparation cannot finish, the successful upload result instead has prepared: false, the original file’s path, and warning: "Uploaded original video. Some browsers may not play this format." CLI warnings print on stderr; JSON success data includes the warning without mixing progress text into stdout. Access, invalid source and site-ownership errors remain errors.

frontpage media list and the website agent’s asset tool show a completed prepared copy instead of its exact original. Separate uploads remain separate even if they had the same display filename. Originals remain stored and follow existing unused-file retention rules. Upgrade an older CLI to receive upload-time preparation.

Workflow reminder

frontpage pull                 # or when tipDrift
# edit allowlisted files following this contract
frontpage diff --json
frontpage push --message "…"
# review https://<subdomain>.frontpage.host. Do not publish unless asked

Inspect rendered pages

Use frontpage screenshots when seeing a page helps you review it. Capture does not edit or publish the site. Requires CLI access on Scale, Enterprise or Internal.

frontpage screenshots capture --site SITE --path /pricing --device mobile --wait --json
frontpage screenshots capture --site SITE --path / --capture full_page --wait --json
frontpage screenshots capture --site SITE --path / --capture element --selector '#contact' --wait --json
frontpage screenshots get CAPTURE_ID --site SITE --json

--device accepts desktop (1280 × 800) or mobile (390 × 844). --capture accepts viewport, full_page or element. Element mode requires one matching CSS selector. Defaults are /, desktop and viewport. Full-page captures return ordered image sections, up to 24,000px; check coverage and warnings.

Captures show the built staging revision, including private Frontpage subdomains. They do not show unsaved or unbuilt edits. After a push, use frontpage deploy wait and check the capture’s returned revision. --wait polls for up to 180 seconds; use the returned ID with get if still pending.

Results expire after 30 days and require current site access by the requesting user. Image URLs are authenticated API paths, not public image links. Fetch them from https://app.frontpage.host using the same API bearer credential to receive PNG bytes. Captures allow up to six new requests per site per minute.

After an uncertain response, repeat the same input using the --request-key UUID shown in the error. This recovers the original request. Use a new key for a fresh capture after a failed job. See frontpage help screenshots and SCREENSHOTS.md in the CLI package for the complete reference. Screenshot MCP tools are not available yet.