Skills astro
Deploy multilingual static websites for free on Cloudflare using Astro framework with markdown source files. Use when: (1) Creating new static sites or blogs, (2) Setting up multilingual (i18n) content, (3) Deploying to Cloudflare Pages, (4) Converting markdown to static websites, (5) Setting up free hosting infrastructure.
git clone https://github.com/openclaw/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/bezkom/astro" ~/.claude/skills/openclaw-skills-astro && rm -rf "$T"
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/bezkom/astro" ~/.openclaw/skills/openclaw-skills-astro && rm -rf "$T"
skills/bezkom/astro/SKILL.mdAstro 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 |
|---|---|---|
| , | Default locale at root |
| , | 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 | |
| Build output | |
| Node version | |
| Environment | |
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 |
|---|---|
| Start dev server |
| Build for production |
| Preview production build |
| Generate content collection types |
| Authenticate with Cloudflare |
| 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
frontmatterlang - Run
after creating content collectionsnpx astro sync
Content Collection Type Errors
Run
npx astro sync to generate TypeScript types.
Resources
Scripts
| Script | Description |
|---|---|
| Create multilingual blog posts |
| 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).