ワンクリックで
astro
Astro framework code standards. Use when writing or reviewing Astro components (.astro), content collections, layouts, and pages.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Astro framework code standards. Use when writing or reviewing Astro components (.astro), content collections, layouts, and pages.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Deep interview for a single feature or module brief that produces a final spec or implementation plan.
@nextnode-solutions/logger standards. Reference this skill when a project uses @nextnode-solutions/logger or when logger integration is being added.
Manage n8n workflows on the NextNode automation instance with the local n8n-cli tool.
NextNode brand guidelines — color palette, typography, logo system, and per-project branding rules. Use when doing UI/frontend work on a NextNode project.
NextNode ecosystem hub. Auto-load when working on any NextNode or SaaS project — covers nextnode.toml config, CI workflows, docker-compose rules, and cross-references to package skills.
Audit a NextNode/SaaS project against all NextNode standards — produces a compliance report with pass/fail/missing status for every required item.
| name | astro |
| description | Astro framework code standards. Use when writing or reviewing Astro components (.astro), content collections, layouts, and pages. |
| user-invocable | false |
MANDATORY CO-SKILL: When this skill is active, ALWAYS also load the
structure-astroskill. Thestructure-astroskill defines the canonical project structure for all Astro projects and is non-negotiable. Ifstructure-astrois not already loaded, load it immediately before proceeding with any Astro work.
.astro components over framework components when no interactivity neededclient:load — interactive immediately on page load (modals, nav menus)client:idle — interactive after page idle (below-fold widgets)client:visible — interactive when scrolled into viewport (counters, carousels)client:only="react" — client-render only, skip SSR (browser-only APIs)client:* to a component that doesn't need interactivityclient:visible or client:idle over client:load for performancesrc/content.config.ts using Zod via astro/zoddefineCollection + z.object({}) — NEVER untyped frontmatterreference() for cross-collection relationshipsglob() for file-based, file() for single-file data sourcesgetCollection() / getEntry() — NEVER raw file readsimport { defineCollection, reference } from 'astro:content';
import { glob } from 'astro/loaders';
import { z } from 'astro/zod';
const blog = defineCollection({
loader: glob({ pattern: '**/*.md', base: './src/data/blog' }),
schema: z.object({
title: z.string(),
pubDate: z.coerce.date(),
author: reference('authors'),
}),
});
---) for server-only logic: imports, data fetching, propsAstro.props with explicit interfacesrc/pages/[slug].astro with getStaticPaths() for static.ts files in src/pages/api/ returning Response objectsoutput: 'static') unless SSR is requiredimport.meta.env works under the hoodimport.meta.env.X becomes the literal value in the output JSPUBLIC_ vars are always inlined into both client and server bundles (by design)staticImportMetaEnv): non-public import.meta.env.SECRET is replaced with process.env.SECRET (runtime) — this is safe but will changeexperimental.staticImportMetaEnv (default in Astro 6.0): ALL import.meta.env values are inlined, including secrets — the literal secret value ends up in the server JS bundleimport.meta.env object into the bundle (fixed, but illustrates the risk)process.env.X is always a runtime lookup — the value never appears in any bundleastro:env/server with access: "secret" for sensitive values — this is the only future-proof patternimport.meta.env.SECRET_X for sensitive data — safe today, broken in Astro 6process.env in Astro — Vite replaces process.env.X with ({}).X in some contexts, and .env files are not auto-loaded into process.envPUBLIC_ vars are fine via import.meta.env.PUBLIC_X — they are meant to be public// astro.config.mjs
import { defineConfig, envField } from "astro/config";
export default defineConfig({
env: {
schema: {
API_SECRET: envField.string({ context: "server", access: "secret" }),
PORT: envField.number({ context: "server", access: "public", default: 4321 }),
},
validateSecrets: true, // validate at startup, useful in CI
},
});
// src/lib/my-service.ts
import { API_SECRET } from "astro:env/server";
// API_SECRET is: never in client bundle, never inlined, always runtime, type-safe, validated
astro:env/server is FORBIDDENastro:env/server can ONLY be imported in server-only files (.astro frontmatter, middleware, API routes). If a .ts file is imported by BOTH server code AND client-side framework components (React, Svelte, etc.), importing astro:env/server will crash the browser with:
The "astro:env/server" module is only available server-side.
Fix: Use astro:env/client for vars needed in shared files. For server-only vars in a shared file, use import.meta.env.X (which Vite strips/replaces correctly per context).
// src/lib/supabase.ts — imported by BOTH server and client code
import { SUPABASE_ANON_KEY } from "astro:env/client"; // works everywhere
export function createServerSupabase(context) {
// SUPABASE_URL is server-only — use import.meta.env, NOT astro:env/server
return createServerClient(import.meta.env.SUPABASE_URL, SUPABASE_ANON_KEY, { ... });
}
export function createBrowserSupabase() {
return createBrowserClient(window.location.origin, SUPABASE_ANON_KEY, { ... });
}
PUBLIC_ prefix duplication with astro:envimport.meta.env.PUBLIC_X requires a PUBLIC_-prefixed env var to expose values to client JS. This forces duplication when infra generates X (not PUBLIC_X).
astro:env eliminates this. Define context: "client", access: "public" in the schema — Astro exposes the var to the browser using the ORIGINAL env var name. No PUBLIC_ prefix, no mapping, no duplication.
// astro.config.ts
import { defineConfig, envField } from "astro/config";
export default defineConfig({
env: {
schema: {
DB_URL: envField.string({ context: "server", access: "public" }),
ANON_KEY: envField.string({ context: "client", access: "public" }),
},
},
});
ANON_KEY is available client-side via import { ANON_KEY } from "astro:env/client" — no PUBLIC_ANON_KEY neededANON_KEY as-is — one name everywhereARG ANON_KEY) so Astro bakes it into client JS| Pattern | Safe for secrets? | Future-proof? |
|---|---|---|
import { X } from "astro:env/server" (access: "secret") | Yes | Yes |
import { X } from "astro:env/client" (access: "public") | Public only | Yes |
import.meta.env.SECRET (current Astro default) | Yes (today) | No (inlined in Astro 6) |
process.env.SECRET | Yes (runtime) | Fragile (Vite may rewrite) |
import.meta.env.PUBLIC_X | Public only | Legacy — prefer astro:env/client |
is:inlineIn Astro, <script> tags are processed by Vite by default — they get bundled, hoisted, and treated as ES modules. This is NOT what you want for external CDN scripts.
When Vite processes a <script src="https://cdn.example.com/lib.js"> tag:
Origin: http://localhost:4321cdn.tailwindcss.com) return a 302 redirect (e.g., to a versioned URL)Access-Control-Allow-Origin headers[Error] Cross-origin redirection to https://cdn.tailwindcss.com/3.4.17
denied by Cross-Origin Resource Sharing policy:
Origin http://localhost:4321 is not allowed by Access-Control-Allow-Origin.
Status code: 302
<script src="https://..."> MUST have is:inline — this tells Astro to emit the tag verbatim, bypassing Vite entirely:
<!-- WRONG — Vite processes this, CORS breaks -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- CORRECT — emitted as-is, browser loads normally -->
<script is:inline src="https://cdn.tailwindcss.com"></script>
<link> stylesheets that Vite might try to processis:inline scripts cannot use TypeScript, imports, or Vite features — they are raw browser scriptsNEVER use the Tailwind Play CDN (cdn.tailwindcss.com) for production. It:
is:inlineProper setup:
pnpm astro add tailwind
This installs @astrojs/tailwind + tailwindcss, patches astro.config.mjs, and generates CSS at build time with full tree-shaking — zero runtime CDN dependency.
If CDN is required (e.g., quick prototype, spec explicitly says CDN):
<script is:inline src="https://cdn.tailwindcss.com"></script>
When reviewing Astro code, flag as critical: bug if:
<script src="https://..."> lacks is:inline — it WILL break in dev with CORS errorsis:inline — page renders unstyledFlag as important: security if:
crossorigin="anonymous" (error reporting is opaque)<style> blocks in .astro files by defaultis:global only when scoped styles genuinely can't work