Marketing site
apps/site is your public website: a home page, pricing, about, contact, a blog and the legal
pages. It is built into plain files, served by Caddy, and links into the app for signing up.
Put your words on it
Section titled “Put your words on it”Every word is in apps/site/src/config.ts: the tagline, the features, the steps, the FAQ, the
about page, your company’s name and address. The pages only arrange it. The product’s name is
appConfig.name from the contracts, as everywhere else.
The home page is laid out to turn visitors into sign-ups, in this order: the hero (one promise,
one button, what stops people from trying, a picture of the product), a strip of short
promises, what customers say (proof), features with pictures (showcase) and the rest in a
grid, how it works, pricing with the recommended plan marked, the FAQ, and a last call to action.
Keep the order and change the words.
- Pictures.
hero.screenshotand eachshowcaseitem point at a file inapps/site/public/screens/. They show the kit’s own screens: replace them with yours. Give each adarkpicture of the same screen in dark mode, and it is shown when the page is dark. - Proof.
proof.logosandproof.quotesare empty, and the section is hidden until you fill one. Only real customers, with their permission. - The free plan. The line under the buttons and the first promise say who the free plan is for, from your plans. Without a free plan they say something else.
Prices are not there. The pricing section reads PLANS from packages/contracts/src/billing.ts,
the same plans the API bills with, so the site cannot show a price your customers are not charged.
Change a plan there and both follow. The plan marked “Most popular” is recommendedPlan() from the same
file, so the site and the app recommend the same one.
Add a page
Section titled “Add a page”Create apps/site/src/pages/<name>.astro with the Base layout, and add it to pages in
apps/site/src/lib/pages.ts with its title and description: that is what draws its share
image. Link it from nav in the config, or from the footer in src/layouts/Base.astro.
For a page that is mostly text, write <name>.mdx with layout: ../layouts/Legal.astro in its
frontmatter, as terms.mdx does.
Write a post
Section titled “Write a post”Add apps/site/src/content/blog/<slug>.mdx:
---title: What we shipped in Octoberdescription: One sentence, shown in the list and when the post is shared.publishedAt: 2026-10-31tags: [product, changelog]author: Ada Lovelace---
The post, in Markdown.draft: true keeps it out of the built site while you write. Each tag gets a page at
/blog/tags/<tag>, and every post is in the feed at /rss.xml and in the sitemap.
Share images
Section titled “Share images”Every page, post and tag page gets an image for when it is shared, drawn at build time from its
title and description (src/lib/og.ts). Change the colours or layout there once, and every
image follows.
Turn on the contact form
Section titled “Turn on the contact form”Set CONTACT_EMAIL to where messages should go. Each one arrives by email through the job
queue, with “Reply” going to the sender. Without it, the form says it is not set up.
The form posts to /api/contact on the site itself. Caddy in the site’s image passes that one
route to the API over the private network, so the API stays private and there is no CORS to set
up; locally the Astro dev server does the same. One address can send 5 messages an hour, and a
field people never see catches most bots.
The legal pages
Section titled “The legal pages”terms.mdx and privacy.mdx are a starting point, not legal advice. The privacy policy
describes what the kit does out of the box; add every service and kind of data your product
uses, and have a lawyer review both for the places you sell in.
Running a public demo
Section titled “Running a public demo”Build the site with PUBLIC_DEMO_RETURN_URL set, and every page shows a bar saying it is a demo,
with a link back to that address. On Railway, DEMO_MODE=true and DEMO_RETURN_URL in
.env.railway set it for the app and the site together.
Add DEMO_ALL_FEATURES=true and everyone gets every feature and the top plan’s allowances
without paying, so visitors try the whole product. The billing page still shows their real
plan, and paying with the provider’s test card still works.