Most Astro sites need a privacy policy before they launch. The usual approach: grab a template from the internet, paste it into a static page, and forget about it until a lawyer asks why it still says “Company Name Here.”
OpenPolicy treats your policies like code. You define them as TypeScript objects, and the Astro integration compiles them to Markdown at build time — in sync with every deploy.
Install
npx astro add @openpolicy/astro
bun add @openpolicy/sdk
astro add installs the package and automatically updates astro.config.mjs. If you prefer to wire it up manually:
bun add -D @openpolicy/astro @openpolicy/vite
Add the integration to astro.config.mjs
import { defineConfig } from "astro/config";
import { openPolicy } from "@openpolicy/astro";
export default defineConfig({
integrations: [
openPolicy({
formats: ["markdown"],
outDir: "src/generated/policies",
}),
],
});
On the first bun run dev, if the config file doesn’t exist yet, the plugin scaffolds it automatically. Edit the generated file and save — the plugin watches for changes and regenerates.
Define your policies
Create a single openpolicy.ts at the root of your project. The unified defineConfig() lets you define all policies in one file with a shared company block:
// openpolicy.ts
import { defineConfig } from "@openpolicy/sdk";
export default defineConfig({
company: {
name: "Acme",
legalName: "Acme, Inc.",
address: "123 Main St, San Francisco, CA 94105",
contact: "privacy@acme.com",
},
effectiveDate: "2026-03-05",
jurisdictions: ["us", "eu"],
dataCollected: {
"Account information": ["Email address", "Display name"],
"Usage data": ["Pages visited", "Session duration"],
},
legalBasis: ["legitimate_interests", "consent"],
retention: {
"Account data": "Until account deletion",
"Analytics data": "13 months",
},
thirdParties: [
{ name: "Vercel", purpose: "Hosting and edge delivery" },
{ name: "Plausible", purpose: "Privacy-friendly analytics" },
],
cookies: {
essential: true,
analytics: true,
marketing: false,
},
});
What gets generated
After the next build (or on save in dev), the plugin writes:
src/generated/policies/
privacy-policy.md
Because the files land inside src/, Astro can import them directly as Markdown components.
Render on a dedicated page
---
// src/pages/privacy.astro
import { Content } from "../../generated/policies/privacy-policy.md";
---
<div class="prose prose-gray max-w-none">
<Content />
</div>
Astro compiles the Markdown to HTML at build time, so there’s no client-side rendering overhead. The prose class (from Tailwind Typography) handles all the heading, list, and paragraph styles.
Add .gitignore entries so the generated files aren’t checked in:
# .gitignore
src/generated/
Why this is better than a static page
- Type-safe. Every field is checked by TypeScript. You can’t ship a policy with a missing contact email.
- Structured. Each section is generated from your actual config — no stale placeholder text.
- Version-controlled. The config lives in your repo.
git blameshows you when and why anything changed. - Jurisdiction-aware. Set
jurisdictions: ["eu"]and GDPR-required sections (right to erasure, data transfers, DPA contact) are included automatically.
The generated Markdown includes all required sections for the jurisdictions you specify. You own the config; OpenPolicy handles the legal structure.