Skip to content

Configuration

This guide covers all configuration options for your Podcast Framework project, including environment variables, Astro configuration, and Sanity setup.

Environment variables are stored in a .env file in your project root.

These variables are required for the framework to work:

Terminal window
# Sanity CMS (Required)
PUBLIC_SANITY_PROJECT_ID="your-sanity-project-id"
PUBLIC_SANITY_DATASET="production"
PUBLIC_SANITY_API_VERSION="2024-01-01"
# Site Configuration (Required)
PUBLIC_SITE_URL="http://localhost:4321"

Add these as needed for additional features:

Terminal window
CONVERTKIT_API_KEY="your-api-key"
CONVERTKIT_FORM_ID="your-form-id"

Where to get:

  1. Sign up at convertkit.com
  2. API Key: Settings → Advanced → API Secret
  3. Form ID: Forms → Select form → Form ID in URL
Terminal window
RESEND_API_KEY="re_..."
NOTIFICATION_EMAIL="[email protected]"

Where to get:

  1. Sign up at resend.com
  2. Create API key in dashboard
  3. Set notification email to where you want contributions sent
Terminal window
PUBLIC_GA_MEASUREMENT_ID="G-XXXXXXXXXX"

Where to get:

  1. Create GA4 property at analytics.google.com
  2. Admin → Data Streams → Web → Measurement ID
Terminal window
SENTRY_DSN="https://[email protected]/..."
SENTRY_ENVIRONMENT="production"

Where to get:

  1. Create project at sentry.io
  2. Settings → Client Keys (DSN)
Terminal window
RSS_FEED_URL="https://yourpodcast.com/feed.xml"

Used by npm run import:episodes to import existing episodes.

Podcast Framework follows Astro’s environment variable conventions:

Public Variables (accessible in browser):

Terminal window
PUBLIC_SANITY_PROJECT_ID="..."
PUBLIC_SITE_URL="..."
PUBLIC_GA_MEASUREMENT_ID="..."

Private Variables (server-only):

Terminal window
RESEND_API_KEY="..."
CONVERTKIT_API_KEY="..."
SENTRY_DSN="..."

Use different .env files for different environments:

Terminal window
.env # Local development
.env.production # Production (not committed)
.env.template # Template (committed to git)

Production deployment:

  1. Don’t commit .env or .env.production
  2. Set variables in your hosting platform (Cloudflare, Netlify, etc.)
  3. Commit .env.template so team knows what’s needed

Edit astro.config.mjs to configure Astro:

import { defineConfig } from 'astro/config';
import tailwind from '@astrojs/tailwind';
export default defineConfig({
site: 'https://yourpodcast.com',
output: 'static',
integrations: [tailwind()],
});

Your production URL. Required for:

  • Sitemap generation
  • RSS feeds
  • Canonical URLs
site: 'https://yourpodcast.com'

Rendering mode:

// Static Site Generation (recommended)
output: 'static'
// Hybrid: SSG + on-demand rendering
output: 'hybrid'
// Full server-side rendering
output: 'server'

Control CSS inlining:

build: {
inlineStylesheets: 'auto' // Auto-inline small CSS files
}

Options:

  • 'auto' - Inline small stylesheets automatically
  • 'always' - Inline all stylesheets
  • 'never' - Never inline (separate CSS files)

Deploy to a subdirectory:

base: '/podcast',
// Site will be at https://yoursite.com/podcast
trailingSlash: 'ignore' // /about or /about/ both work
trailingSlash: 'always' // Always /about/
trailingSlash: 'never' // Always /about
image: {
service: {
entrypoint: 'astro/assets/services/sharp',
config: {
limitInputPixels: false,
},
},
}

Edit tailwind.config.mjs to customize Tailwind:

export default {
content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
'./node_modules/@rejected-media/podcast-framework-core/**/*.astro',
],
theme: {
extend: {},
},
plugins: [],
};

Extend the default theme:

theme: {
extend: {
colors: {
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
// ... more shades
900: '#0c4a6e',
}
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
serif: ['Merriweather', 'serif'],
},
},
}

Add Tailwind plugins:

plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
],

Install first:

Terminal window
npm install @tailwindcss/typography @tailwindcss/forms

Edit tsconfig.json for TypeScript settings:

{
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react",
"baseUrl": ".",
"paths": {
"@components/*": ["src/components/*"],
"@lib/*": ["src/lib/*"]
}
}
}

Use path aliases for cleaner imports:

// Without aliases
import { helper } from '../../../../lib/utils';
// With aliases
import { helper } from '@lib/utils';

Configure in tsconfig.json:

"paths": {
"@components/*": ["src/components/*"],
"@lib/*": ["src/lib/*"],
"@layouts/*": ["src/layouts/*"]
}

Edit sanity/sanity.config.ts for Sanity Studio:

import { defineConfig } from 'sanity';
import { structureTool } from 'sanity/structure';
import { visionTool } from '@sanity/vision';
import { schemaTypes } from './schemas';
export default defineConfig({
name: 'default',
title: 'My Podcast',
projectId: process.env.PUBLIC_SANITY_PROJECT_ID!,
dataset: process.env.PUBLIC_SANITY_DATASET || 'production',
plugins: [
structureTool(),
visionTool(),
],
schema: {
types: schemaTypes,
},
});
import { defineConfig } from 'sanity';
export default defineConfig({
// ... other config
theme: {
fonts: {
heading: {
family: 'Inter, sans-serif',
},
},
colors: {
primary: '#3B82F6',
},
},
});
import { defineConfig } from 'sanity';
export default defineConfig({
// ... other config
document: {
actions: (prev, context) => {
// Customize document actions
return prev;
},
},
});

Add custom scripts to package.json:

{
"scripts": {
"dev": "astro dev",
"dev:sanity": "cd sanity && npx sanity dev",
"build": "astro check && astro build",
"preview": "astro preview",
"import:episodes": "node scripts/import-episodes.js",
"init:theme": "node scripts/init-theme.js",
"upload:photos": "node scripts/upload-guest-photos.js"
}
}

Create scripts in scripts/ directory:

scripts/my-script.js
import { getPodcast } from '@rejected-media/podcast-framework-core';
async function myScript() {
const podcast = await getPodcast();
console.log(podcast);
}
myScript();

Run with:

Terminal window
node scripts/my-script.js

Create _redirects in public/:

/api/* /api/:splat 200
/old-page /new-page 301

Create netlify.toml:

[build]
command = "npm run build"
publish = "dist"
[[redirects]]
from = "/old-page"
to = "/new-page"
status = 301

Create vercel.json:

{
"buildCommand": "npm run build",
"outputDirectory": "dist",
"devCommand": "npm run dev",
"installCommand": "npm install"
}
astro.config.mjs
export default defineConfig({
image: {
service: {
entrypoint: 'astro/assets/services/sharp',
},
},
});
astro.config.mjs
export default defineConfig({
prefetch: {
defaultStrategy: 'hover',
prefetchAll: true,
},
});
astro.config.mjs
export default defineConfig({
build: {
inlineStylesheets: 'auto',
assets: '_astro',
},
vite: {
build: {
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks: (id) => {
if (id.includes('node_modules')) {
return 'vendor';
}
},
},
},
},
},
});

Add to .gitignore:

.env
.env.local
.env.production

Commit template:

.env.template # ✅ Safe to commit

Don’t hardcode:

// ❌ Bad
const API_KEY = 'sk_live_abc123';
// ✅ Good
import { getEnv } from '@rejected-media/podcast-framework-core';
const API_KEY = getEnv('API_KEY');

Update .env.template with all variables:

Terminal window
# Required for CMS
PUBLIC_SANITY_PROJECT_ID=
# Optional - Newsletter
CONVERTKIT_API_KEY=
CONVERTKIT_FORM_ID=

Check variables at build time:

src/lib/config.ts
import { getRequiredEnv } from '@rejected-media/podcast-framework-core';
export const config = {
sanity: {
projectId: getRequiredEnv('PUBLIC_SANITY_PROJECT_ID'),
dataset: getRequiredEnv('PUBLIC_SANITY_DATASET'),
},
};

getRequiredEnv() throws if variable is missing.

Check .env file exists and contains the variable:

Terminal window
cat .env | grep VARIABLE_NAME

Verify PUBLIC_SANITY_PROJECT_ID matches your Sanity project:

Terminal window
npx sanity projects list

Ensure tailwind.config.mjs includes framework paths:

content: [
'./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}',
'./node_modules/@rejected-media/podcast-framework-core/**/*.astro', // ✅ Required
]

Check TypeScript version:

Terminal window
npm list typescript

Update if needed:

Terminal window
npm install typescript@latest