An Astro landing page without a page builder
Page builders are quick until you want something they did not plan for. This is the other route — a real Astro project you own, start to finish, in about an evening.
The pitch for a page builder is that you avoid code. The bill arrives later: a subscription, an export that produces markup nobody wants to maintain, and a hard stop the first time the design needs something the builder does not offer.
A hand-built Astro page takes an evening and leaves you with files you own. Here is the whole path.
Start the project
npm create astro@latest my-site
cd my-site
npm run devChoose the empty template. You want a blank canvas, not a demo blog to delete. The dev server prints a local URL and reloads as you save.
Set your tokens before your sections
This is the step people skip and regret. Before writing a single section, decide the handful of values every section will read — colours, the type scale, spacing rhythm, corner radius, maximum content width. Put them in your layout as custom properties.
<style is:global>
:root {
--bg: #ffffff;
--text: #21201c;
--accent: #5b5bd6;
--maxw: 1440px;
--radius-card: 16px;
}
body { background: var(--bg); color: var(--text); }
</style>Do this first and a rebrand later is one file. Skip it and it is a search-and-replace across every section you have written, which is how sites end up with four slightly different greys.
Build the page from sections
A landing page is a stack: navigation, hero, features, social proof, pricing, FAQ, footer. Give each one its own file in src/components/sections/, then assemble them in src/pages/index.astro.
---
import Layout from '../layouts/Layout.astro';
import Hero from '../components/sections/Hero.astro';
import Features from '../components/sections/Features.astro';
---
<Layout title="My studio">
<main>
<Hero title="We build calm, fast websites" />
<Features items={[ /* … */ ]} />
</main>
</Layout>Keep content in props rather than in the markup. It takes no extra effort while you are writing the section and saves you from hunting through HTML when the copy changes — which it will, twice, before launch.
Handle images so they do not rot
Never hardcode a photo inside a section. Accept it as a prop and default to a placeholder that ships with the project:
---
const { image = '/placeholder.svg', imageAlt = '' } = Astro.props;
---
<img src={image} alt={imageAlt} loading="lazy" />Now a section copied into another project renders immediately instead of pointing at a file that does not exist there. Put real files in public/ and reference them with an absolute path.
Deploy it
Astro builds to static HTML, so hosting is close to free and close to instant. Push the repo to GitHub and connect it to Netlify, Vercel or Cloudflare Pages; the build command is npm run build and the output directory is dist.
One detail worth pinning: set the Node version explicitly in your host config. Recent Astro needs Node 22 or newer, and a host defaulting to an older one fails the build while quietly serving the previous deploy — the site looks fine and your change never appears.
What you end up with
Static HTML with almost no JavaScript, hosting you can move in an afternoon, and every file under your control. No subscription, no export step, and no ceiling on what the design can do.
The only real cost is the first evening — and unlike the builder route, you only pay it once.