A static site is rendered to HTML, CSS and JavaScript when it is built, and served as files from then on: portfolios, brochure sites, event pages and small blogs. With no code running on a server, it can be hosted for free on a CDN and will never go down for want of a patch.
| Concern | Choice |
|---|---|
| Generator | Astro for content-first sites, or Next.js with a static export |
| Styling | Tailwind CSS |
| Hosting | Cloudflare Pages, or GitHub Pages |
| Deploys | The host’s Git integration, or GitHub Actions |
Its neighbours in this series draw the line: a page that needs a form or a server function is a landing page, on Vercel, and reference documentation is a documentation site, in Hugo.
Building one
Astro sends no JavaScript to the browser unless a component asks for it, which suits sites that are mostly content:
npm create astro@latest my-site
cd my-site
npx astro add tailwind
npm run build # static files in ./distWhen a site starts from Next.js, for its components or because it may grow into a landing page later, a static export gives the same kind of output:
// next.config.ts
const nextConfig = { output: "export" };
export default nextConfig;next build then writes plain HTML to out/, at the cost of the features that
need a server, such as route handlers and image optimisation at request time.
Cloudflare Pages
Cloudflare Pages is the default host, because my domains’ DNS is already on Cloudflare: the CDN, the certificate and the DNS record come wired together, and the free tier has no bandwidth limit.
- In Cloudflare’s dashboard, create a Pages project connected to the GitHub repository.
- Set the build command,
npm run build, and the output directory,distfor Astro oroutfor Next.js. - Add the custom domain. Every push to
maindeploys, and every pull request gets a preview URL.
GitHub Pages
For a site that belongs with its repository, GitHub Pages works as well, with a workflow that builds the site and hands it to Pages:
# .github/workflows/pages.yml
name: Deploy static site
on: { push: { branches: [main] } }
permissions: { pages: write, id-token: write, contents: read }
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 24 }
- run: npm ci && npm run build
- uses: actions/upload-pages-artifact@v3
with: { path: dist }
deploy:
needs: build
runs-on: ubuntu-latest
environment: github-pages
steps:
- uses: actions/deploy-pages@v4In the repository’s settings, Pages’ source must be set to GitHub Actions for the deploy job to publish.