{"id":49549543,"url":"https://github.com/vstorm-co/astro-blog","last_synced_at":"2026-05-02T21:32:19.554Z","repository":{"id":353668260,"uuid":"1219867662","full_name":"vstorm-co/astro-blog","owner":"vstorm-co","description":"Astro 6 blog template with built-in browser admin panel. Write \u0026 publish MDX posts without touching a text editor. 100/100 Lighthouse. Zero JS on reading pages.","archived":false,"fork":false,"pushed_at":"2026-04-24T23:08:56.000Z","size":3377,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-04-25T00:30:45.554Z","etag":null,"topics":["admin-panel","astro","astro-blog","blog","blog-template","dark-mode","lighthouse","markdown","mdx","react","ssg","static-site-generator","tailwindcss","typescript","vercel","vstorm"],"latest_commit_sha":null,"homepage":"https://astro-blog-kappa-rouge.vercel.app","language":"Astro","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vstorm-co.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"vstorm-co"}},"created_at":"2026-04-24T09:51:22.000Z","updated_at":"2026-04-24T23:04:38.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vstorm-co/astro-blog","commit_stats":null,"previous_names":["vstorm-co/astro-blog"],"tags_count":1,"template":true,"template_full_name":null,"purl":"pkg:github/vstorm-co/astro-blog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vstorm-co%2Fastro-blog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vstorm-co%2Fastro-blog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vstorm-co%2Fastro-blog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vstorm-co%2Fastro-blog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vstorm-co","download_url":"https://codeload.github.com/vstorm-co/astro-blog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vstorm-co%2Fastro-blog/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32550900,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-02T21:31:48.061Z","status":"ssl_error","status_checked_at":"2026-05-02T21:31:46.574Z","response_time":132,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["admin-panel","astro","astro-blog","blog","blog-template","dark-mode","lighthouse","markdown","mdx","react","ssg","static-site-generator","tailwindcss","typescript","vercel","vstorm"],"created_at":"2026-05-02T21:32:15.473Z","updated_at":"2026-05-02T21:32:19.546Z","avatar_url":"https://github.com/vstorm-co.png","language":"Astro","funding_links":["https://github.com/sponsors/vstorm-co"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eastro-blog 📝\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\nThe blog template that ships with a \u003cb\u003ebrowser-based admin panel\u003c/b\u003e — write and publish MDX posts without touching a text editor.\u003cbr\u003e\nBuilt on \u003cb\u003eAstro 6 + Tailwind CSS 4\u003c/b\u003e, zero JS on reading pages, 100 Lighthouse across all four categories.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/vstorm-co/astro-blog/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/vstorm-co/astro-blog/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://astro.build\"\u003e\u003cimg src=\"https://astro.badg.es/v2/built-with-astro/tiny.svg\" alt=\"Made with Astro\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://nodejs.org\"\u003e\u003cimg src=\"https://img.shields.io/badge/node-%3E%3D20-brightgreen?logo=node.js\u0026logoColor=white\" alt=\"Node ≥ 20\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://bun.sh\"\u003e\u003cimg src=\"https://img.shields.io/badge/bun-%3E%3D1-f9f1e1?logo=bun\u0026logoColor=black\" alt=\"Bun ≥ 1\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-yellow.svg\" alt=\"License: MIT\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/vstorm-co/astro-blog/blob/main/SECURITY.md\"\u003e\u003cimg src=\"https://img.shields.io/badge/security-policy-blueviolet?logo=shieldsdotio\u0026logoColor=white\" alt=\"Security Policy\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://x.com/Kacper95682155\"\u003e\u003cimg src=\"https://img.shields.io/badge/X-000000?logo=x\u0026logoColor=white\" alt=\"X\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://astro-blog-kappa-rouge.vercel.app/\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/🖥️%20Live%20Demo-Visit%20Site-6d4fe8?style=for-the-badge\" alt=\"Live Demo\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003ctable width=\"100%\"\u003e\u003ctr\u003e\n  \u003ctd width=\"50%\"\u003e\u003cimg src=\"./assets/hero_dark.png\" alt=\"Dark mode\" width=\"100%\"\u003e\u003c/td\u003e\n  \u003ctd width=\"50%\"\u003e\u003cimg src=\"./assets/hero_light.png\" alt=\"Light mode\" width=\"100%\"\u003e\u003c/td\u003e\n\u003c/tr\u003e\u003c/table\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./assets/admin_panel.png\" alt=\"astro-blog — admin panel\" width=\"100%\"\u003e\n\u003c/p\u003e\n\n---\n\n## 🚀 Quick Start\n\n```bash\nbunx degit vstorm-co/astro-blog my-blog \u0026\u0026 cd my-blog \u0026\u0026 bun install \u0026\u0026 bun run dev\n```\n\nOpen **http://localhost:4321/admin/** — your blog editor is ready.\n\n\u003e **npm users:** `npx degit vstorm-co/astro-blog my-blog \u0026\u0026 cd my-blog \u0026\u0026 npm install \u0026\u0026 npm run dev`\n\n---\n\n## ✨ Features\n\n- 🛠 **Built-in admin panel** — browser-based MDX editor with live preview, autosave, drag-and-drop images, and a component palette. Stripped from every production build.\n- ⚡ **Zero JS on reading pages** — no framework runtime, no hydration cost. Home page ships 0 KB of JavaScript.\n- 📝 **MDX-first** with 22 ready-made blog components: `Callout`, `Figure`, `Stat`, `Timeline`, `Terminal`, `FileTree`, `Diff`, `Gallery`, `Tweet`, `Bookmark`, and more\n- 🎨 **Single-file config** — title, author, nav, socials, accent colour, feature flags all in `src/site.config.ts`\n- 🌗 **Dark + light mode** with pre-paint init (zero FOUC) and `prefers-color-scheme` fallback\n- ✍️ **Scheduled posts** — set a future `pubDate`, post goes live on the next build after that time\n- 🏷️ **Tag pages** auto-generated from frontmatter\n- 📡 **RSS feed, sitemap, robots.txt** built at compile time\n- 🚀 **One-click deploy** to Vercel, Netlify, or Cloudflare Pages\n- 🔷 **TypeScript + Zod schemas** — typed frontmatter, zero runtime surprises\n- 🔒 **ESLint + Prettier + Husky** — enforced code style from the first commit\n\n---\n\n## ✅ Lighthouse Score\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./assets/lighthouse.png\" alt=\"astro-blog Lighthouse score — 100 Performance, 100 Accessibility, 100 Best Practices, 100 SEO\" width=\"710\"\u003e\n\u003c/p\u003e\n\n---\n\n## 🆚 Why astro-blog?\n\n|                              | **astro-blog** | AstroPaper | Fuwari | Raw Astro |\n| ---------------------------- | :------------: | :--------: | :----: | :-------: |\n| Admin panel (browser editor) |       ✅       |     ❌     |   ❌   |    ❌     |\n| Zero JS on reading pages     |       ✅       |     ❌     |   ❌   |    ✅     |\n| 100 Lighthouse (all 4)       |       ✅       |     ✅     |   —    |     —     |\n| 22 MDX components            |       ✅       |     ❌     |   ❌   |    ❌     |\n| Drag-and-drop image upload   |       ✅       |     ❌     |   ❌   |    ❌     |\n| Scheduled posts              |       ✅       |     ❌     |   ❌   |    ❌     |\n| Single-file config           |       ✅       |     ✅     |   ✅   |    ❌     |\n| Dark + light mode            |       ✅       |     ✅     |   ✅   |     —     |\n| One-click deploy             |       ✅       |     ✅     |   ✅   |     —     |\n\nThe admin panel is the differentiator — no other Astro blog template ships one.\n\n---\n\n## 🛠 First 10 Minutes\n\nAfter cloning, open `src/site.config.ts` and work through this list:\n\n1. **Set your identity** — `website`, `title`, `author`, `description`, `accent`\n2. **Update socials** — edit `src/socials.ts`, keep only the providers you use\n3. **Rewrite the About page** — `src/data/pages/about.mdx` or use the admin panel\n4. **Delete example posts** — remove everything in `src/data/blog/` and write your own\n5. **Replace the favicon** — drop your SVG at `public/favicon.svg`\n6. **Set an OG image** — 1200×630 PNG at `public/og-default.png` (or run `python3 scripts/generate-og.py`)\n7. **Deploy** — push to GitHub and click one of the deploy buttons below\n8. **Protect `main`** — on GitHub: require PR + CI green before merge\n9. **Turn on Discussions** — for open-ended questions from readers\n10. **Fill `.github/FUNDING.yml`** — if you accept sponsorship\n\n---\n\n## 🖊️ Writing Posts\n\n### Option A — Admin panel (recommended)\n\nStart the dev server and open `localhost:4321/admin/`:\n\n- **Split-pane MDX editor** with autosave (2 s debounce, `Cmd+S` forces save)\n- **Component palette** — click Insert, snippet + import land at your cursor\n- **Image drag-and-drop** — files written to `public/images/blog/\u003cslug\u003e/`\n- **Live preview** inside an iframe of the real post layout\n- **Rename slug / delete post** from the sidebar\n\nThe admin panel is dev-only — production builds 404 the route.\n\n### Option B — CLI\n\n```bash\nbun run new-post \"My post title\"\n# → src/data/blog/my-post-title.mdx  (draft: true by default)\n```\n\n### Frontmatter reference\n\n```yaml\n---\ntitle: \"My post title\"\ndescription: \"One-line summary — used in meta tags and post cards.\"\npubDate: 2026-05-01\nupdatedDate: 2026-05-15 # optional\nauthor: \"Jane Doe\"\ntags: [\"astro\", \"mdx\"]\ndraft: false # excluded from prod builds when true\nfeatured: false # pinned to the home page featured strip\ncover: \"./cover.jpg\" # optional, relative to the MDX file\nogImage: \"/custom-og.png\" # optional per-post OG override\ncanonicalURL: \"https://…\" # optional, for cross-posts\n---\n```\n\n### Scheduling a post\n\nSet `pubDate` to a future date. The post is invisible until the next build after that time. For automatic builds:\n\n1. Add a `DEPLOY_HOOK_URL` repo secret (Vercel / Netlify / CF each provide one).\n2. Set repo variable `SCHEDULED_REBUILD=1`.\n3. `.github/workflows/scheduled-rebuild.yml` hits the hook hourly.\n\n---\n\n## 🧩 MDX Components\n\n22 components grouped by purpose:\n\n| Group         | Components                                               |\n| ------------- | -------------------------------------------------------- |\n| **Prose**     | `Callout` `InfoCard` `PullQuote` `Aside`                 |\n| **Data**      | `Stat` `TwoColumn` `Comparison` `Timeline`               |\n| **Media**     | `Figure` `Gallery` `VideoEmbed` `Tweet` `Bookmark`       |\n| **Technical** | `CodeGroup` `Terminal` `FileTree` `Diff` `Kbd` `Details` |\n| **Structure** | `Steps` `Badge` `Divider`                                |\n\nAll components live in `src/components/blog/`. See them in action in `src/data/blog/mdx-component-reference.mdx`.\n\n### Adding your own component\n\nDrop an `.astro` file in `src/components/blog/` with three annotation comments:\n\n```astro\n{/* @mdx-label MyComponent */}\n{/* @mdx-description One-line description shown in the palette. */}\n{\n  /* @mdx-snippet\n\u003cMyComponent prop=\"example\"\u003e\n  Body text\n\u003c/MyComponent\u003e\n*/\n}\n```\n\nThe admin palette picks it up on the next refresh — no other code changes needed.\n\n---\n\n## ⚙️ Configuration\n\n| File                    | What you change                                           |\n| ----------------------- | --------------------------------------------------------- |\n| `src/site.config.ts`    | Title, author, nav, socials, accent colour, feature flags |\n| `src/socials.ts`        | Social links and share targets                            |\n| `src/styles/global.css` | Design tokens — colours, fonts, radii, shadows            |\n| `src/styles/prose.css`  | Post body typography                                      |\n| `src/data/blog/`        | Blog posts                                                |\n| `src/data/pages/`       | About / Uses / Now / etc.                                 |\n| `public/favicon.svg`    | Favicon                                                   |\n| `public/og-default.png` | Default OG image (1200 × 630 px)                          |\n\n### Feature flags\n\n```ts\nfeatures: {\n  search: false,           // Pagefind-powered /search/ page (requires build step)\n  archive: true,           // /archive/ — chronological post list\n  dynamicOg: false,        // per-post OG via Satori\n  editPost: false,         // \"Edit on GitHub\" link on each post\n  adminPanel: true,        // dev-only admin UI\n  lightAndDarkMode: true,  // theme toggle visible / hidden\n}\n```\n\n---\n\n## ⚡ Commands\n\n| Command                    | Action                               |\n| -------------------------- | ------------------------------------ |\n| `bun run dev`              | Start dev server at `localhost:4321` |\n| `bun run build`            | Type-check + build to `./dist/`      |\n| `bun run preview`          | Preview the production build locally |\n| `bun run new-post \"Title\"` | Scaffold a new draft post            |\n| `bun run optimize-images`  | Resize + convert blog images to WebP |\n| `bun run lint`             | Run ESLint                           |\n| `bun run format`           | Format with Prettier                 |\n| `bun run test`             | Run Vitest unit tests                |\n\n---\n\n## 🗂 Project Structure\n\n```\nsrc/\n├─ site.config.ts       # ← start here: identity, nav, features\n├─ socials.ts\n├─ content.config.ts    # Zod schemas\n├─ data/\n│  ├─ blog/             # MDX posts\n│  └─ pages/            # About, Uses, etc.\n├─ layouts/             # Base / Post / Page\n├─ components/\n│  ├─ blog/             # 22 MDX components\n│  ├─ layout/           # Header / Footer / SEO\n│  ├─ home/             # Hero / LatestPosts / FeaturedStrip\n│  ├─ post/             # PostCard / TOC / Pagination / ShareLinks\n│  ├─ islands/          # React islands (CopyCode, Search, TOC scrollspy)\n│  └─ admin/            # dev-only admin panel\n├─ lib/\n│  ├─ posts.ts          # content helpers\n│  ├─ schemas.ts        # JSON-LD builders\n│  └─ slug.ts\n├─ pages/               # file-based routes\n└─ styles/              # global.css, prose.css\n\npublic/\n├─ favicon.svg\n├─ og-default.png\n├─ _headers / _redirects   # Cloudflare Pages headers\n└─ images/blog/\u003cslug\u003e/     # per-post images (managed by admin)\n\nscripts/\n├─ new-post.ts             # post scaffolder\n├─ optimize-images.py      # WebP optimiser\n└─ generate-og.py          # OG image generator\n```\n\n---\n\n## 🚢 Deploy\n\n| Platform             | One-click                                                                                                                                                      |\n| -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Vercel**           | [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vstorm-co/astro-blog)                        |\n| **Netlify**          | [![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/vstorm-co/astro-blog) |\n| **Cloudflare Pages** | [![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/vstorm-co/astro-blog)    |\n\nSecurity headers and `/admin/*` blockers are pre-configured for all three in `vercel.json`, `netlify.toml`, and `public/_headers`.\n\n---\n\n## 🛠 Tech Stack\n\n|                          |                                                                                                       |\n| ------------------------ | ----------------------------------------------------------------------------------------------------- |\n| **Framework**            | [Astro 6](https://astro.build)                                                                        |\n| **Content**              | [MDX](https://mdxjs.com) + Astro Content Collections                                                  |\n| **Styling**              | [Tailwind CSS 4](https://tailwindcss.com) + CSS custom properties                                     |\n| **Syntax highlighting**  | [Shiki](https://shiki.style) via Expressive Code                                                      |\n| **Editor (admin)**       | [CodeMirror 6](https://codemirror.net)                                                                |\n| **Type checking**        | [TypeScript](https://www.typescriptlang.org) strict mode                                              |\n| **Testing**              | [Vitest](https://vitest.dev)                                                                          |\n| **Linting / formatting** | [ESLint](https://eslint.org) + [Prettier](https://prettier.io)                                        |\n| **Git hooks**            | [Husky](https://typicode.github.io/husky) + [lint-staged](https://github.com/lint-staged/lint-staged) |\n\n---\n\n## ❓ FAQ\n\n**Why Astro, not Next.js?**\nContent-first. Zero JS by default, typed frontmatter via content collections, no runtime framework lock-in.\n\n**Can I use pure Markdown instead of MDX?**\nYes — rename `.mdx` → `.md`. The content loader accepts both. MDX components only work in `.mdx` files.\n\n**How do I add comments?**\nWe don't ship a comment system. [Giscus](https://giscus.app) takes ~10 lines at the bottom of `src/layouts/PostLayout.astro`.\n\n**Is the admin panel safe to expose publicly?**\n**No.** It has no authentication and writes to your filesystem. It binds to `127.0.0.1` in dev and production builds 404 the route.\n\n**Will there be i18n?**\nNot in this template. Use [Astro's i18n recipe](https://docs.astro.build/en/recipes/i18n/) directly.\n\n---\n\n## ⭐ Star History\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.star-history.com/#vstorm-co/astro-blog\u0026type=date\"\u003e\n    \u003cimg src=\"https://api.star-history.com/svg?repos=vstorm-co/astro-blog\u0026type=date\" alt=\"Star History\" width=\"600\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## 🤝 Contributing\n\nSee [`CONTRIBUTING.md`](./CONTRIBUTING.md) for coding conventions, commit style, and how to run the test suite. Issues and PRs welcome.\n\nPlease read [`CODE_OF_CONDUCT.md`](./CODE_OF_CONDUCT.md) before participating. Report security vulnerabilities via [`SECURITY.md`](./SECURITY.md).\n\n## 📄 License\n\n[MIT](./LICENSE) © [Vstorm](https://github.com/vstorm-co)\n\n---\n\n\u003cdiv align=\"center\"\u003e\n\n### Need a custom blog or content platform?\n\n\u003cp\u003eWe're \u003ca href=\"https://vstorm.co\"\u003e\u003cb\u003eVstorm\u003c/b\u003e\u003c/a\u003e — an Applied Agentic AI Engineering Consultancy.\u003cbr\u003e\nastro-blog is the template we use for our own writing. We build production-grade web apps too.\u003c/p\u003e\n\n\u003ca href=\"https://vstorm.co/contact-us/\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Talk%20to%20us%20%E2%86%92-0066FF?style=for-the-badge\u0026logoColor=white\" alt=\"Talk to us\"\u003e\n\u003c/a\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\nBuilt on \u003ca href=\"https://astro.build\"\u003eAstro\u003c/a\u003e · \u003ca href=\"https://tailwindcss.com\"\u003eTailwind CSS\u003c/a\u003e · \u003ca href=\"https://mdxjs.com\"\u003eMDX\u003c/a\u003e · \u003ca href=\"https://shiki.style\"\u003eShiki\u003c/a\u003e · \u003ca href=\"https://codemirror.net\"\u003eCodeMirror\u003c/a\u003e\n\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvstorm-co%2Fastro-blog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvstorm-co%2Fastro-blog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvstorm-co%2Fastro-blog/lists"}