Astro
v1.0.1Deploy multilingual static websites on Cloudflare with Astro using markdown sources, supporting i18n, free hosting, and Git-based or direct deployment.
Like a lobster shell, security has layers — review code before you run it.
License
SKILL.md
Astro Static Site Generator
Deploy multilingual static websites for free on Cloudflare using Astro framework.
Prerequisites
- Node.js 20+ installed
- Cloudflare account (free)
- Git repository (GitHub, GitLab, or Bitbucket)
Quick Start
1. Create Project
npm create astro@latest my-site -- --template minimal
cd my-site
npm install
2. Configure for Cloudflare
Static Sites (Recommended for most use cases)
No adapter needed. Use default static output:
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
site: 'https://your-site.pages.dev',
});
SSR/Edge Functions (Optional)
If you need server-side rendering or edge functions:
npm install @astrojs/cloudflare
// astro.config.mjs
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
output: 'server',
adapter: cloudflare(),
site: 'https://your-site.pages.dev',
});
3. Deploy to Cloudflare
Git Integration (Recommended)
- Push to GitHub/GitLab
- Cloudflare Dashboard → Pages → Create project → Connect to Git
- Configure:
- Build command:
npm run build - Build output:
dist
- Build command:
Direct Upload
# Deploy (authenticate via Cloudflare dashboard or wrangler)
npx wrangler pages deploy dist
Multilingual Configuration
Astro Config
// astro.config.mjs
export default defineConfig({
i18n: {
defaultLocale: 'en',
locales: ['en', 'es', 'fr', 'de'],
routing: {
prefixDefaultLocale: false, // /about instead of /en/about
},
},
});
Routing Modes:
| Setting | URL Structure | Best For |
|---|---|---|
prefixDefaultLocale: false | /about, /es/about | Default locale at root |
prefixDefaultLocale: true | /en/about, /es/about | All locales prefixed |
Content Structure
src/content/
├── config.ts # Content collection schema
└── docs/
├── en/
│ ├── index.md
│ └── guide.md
├── es/
│ ├── index.md
│ └── guide.md
└── fr/
├── index.md
└── guide.md
Content Collection Schema
// src/content/config.ts
import { defineCollection, z } from 'astro:content';
const docs = defineCollection({
type: 'content',
schema: z.object({
title: z.string(),
description: z.string(),
lang: z.enum(['en', 'es', 'fr', 'de']),
}),
});
export const collections = { docs };
Note: Run npx astro sync after adding content collections to generate types.
Language Switcher Component
---
// src/components/LanguageSwitcher.astro
const languages = {
en: 'English',
es: 'Español',
fr: 'Français',
de: 'Deutsch',
};
const currentPath = Astro.url.pathname;
const currentLang = Astro.currentLocale || 'en';
---
<select onchange="window.location = this.value">
{Object.entries(languages).map(([code, name]) => (
<option
value={`/${code}${currentPath}`}
selected={code === currentLang}
>
{name}
</option>
))}
</select>
File Structure
my-site/
├── astro.config.mjs # Astro configuration
├── package.json
├── public/
│ ├── favicon.svg
│ └── _redirects # Cloudflare redirects (optional)
├── src/
│ ├── components/
│ │ └── LanguageSwitcher.astro
│ ├── content/
│ │ ├── config.ts
│ │ └── blog/
│ │ ├── en/
│ │ └── es/
│ ├── layouts/
│ │ └── BaseLayout.astro
│ └── pages/
│ ├── index.astro
│ ├── en/
│ │ └── index.astro
│ └── es/
│ └── index.astro
Cloudflare Pages Settings
| Setting | Value |
|---|---|
| Build command | npm run build |
| Build output | dist |
| Node version | 20 |
| Environment | NODE_VERSION=20 |
Custom Domain
Cloudflare Dashboard → Pages → your-site → Custom domains → Add domain
Redirects
Create public/_redirects:
/ /en/ 302
/old-page /new-page 301
Commands Reference
| Command | Description |
|---|---|
npm run dev | Start dev server |
npm run build | Build for production |
npm run preview | Preview production build |
npx astro sync | Generate content collection types |
npx wrangler login | Authenticate with Cloudflare |
npx wrangler pages deploy dist | Deploy to Cloudflare |
Blog with Content Collections
---
// src/pages/blog/[...slug].astro
import { getCollection } from 'astro:content';
export async function getStaticPaths() {
const posts = await getCollection('blog');
return posts.map(post => ({
params: { slug: post.slug },
props: { post },
}));
}
const { post } = Astro.props;
const { Content } = await post.render();
---
<article>
<h1>{post.data.title}</h1>
<Content />
</article>
Troubleshooting
Build Fails on Cloudflare
Set NODE_VERSION=20 in Cloudflare Pages environment variables.
404 on Nested Routes
// astro.config.mjs
export default defineConfig({
trailingSlash: 'always',
});
i18n Not Working
Ensure:
- Locales match folder names exactly
- Content files have correct
langfrontmatter - Run
npx astro syncafter creating content collections
Content Collection Type Errors
Run npx astro sync to generate TypeScript types.
Resources
Scripts
| Script | Description |
|---|---|
astro-new-post.py | Create multilingual blog posts |
astro-i18n-check.py | Validate translation coverage |
Script Usage
# Create a new post in multiple languages
python scripts/astro-new-post.py --title "My Post" --langs en,es,fr
# Create with author and tags
python scripts/astro-new-post.py --title "Tutorial" --langs en,es --author "John" --tags tutorial,astro
# Check translation coverage
python scripts/astro-i18n-check.py --langs en,es,fr
# Check specific content directory
python scripts/astro-i18n-check.py --content-dir src/content/blog --langs en,es
# Output as JSON
python scripts/astro-i18n-check.py --langs en,es,fr --json
All scripts use only Python standard library (no dependencies).
Files
4 totalComments
Loading comments…
