Skip to content

site.ts reference

src/config/site.ts is the file you’ll edit most. It exports one siteConfig object that components read from at build time. Edit a value, save, and the dev server reloads with the change.

Every theme follows the same structure — once you know one, you know all of them.

src/config/site.ts
export const siteConfig = {
// ─── Site Identity ───────────────────────────────────
name: "Aurora",
tagline: "The modern platform for ambitious teams",
url: "https://example.com",
logo: "", // empty → built-in mark; "/logo.svg" → your file
favicon: "/favicon.svg",
// ─── Navigation ──────────────────────────────────────
navigation: { /* links + CTA */ },
// ─── Contact form / Analytics / Newsletter ───────────
contactForm: { /* Formspree | FormSubmit | Netlify */ },
analytics: { /* Google | Plausible | Umami */ },
newsletter: { /* Mailchimp | ConvertKit | Buttondown */ },
// ─── Social / SEO / Footer ───────────────────────────
social: { twitter, github, linkedin, ... },
seo: { title, description, ogImage, twitterHandle },
footer: { copyright, columns: [...] },
// ─── Section data ────────────────────────────────────
team, video, timeline, comparison,
gallery, carousel, tabs, appDownload, map,
// ─── Custom head scripts (analytics tags, pixels…) ──
headScripts: "",
} as const;
name: "Aurora",
tagline: "The modern platform for ambitious teams",
url: "https://example.com", // used in canonical URLs and the sitemap
logo: "", // path or empty for the built-in mark
favicon: "/favicon.svg",

The url field is consumed by astro.config.mjs and the sitemap integration — set it to your real domain before deploying or sitemap URLs will be wrong.

navigation: {
sticky: true,
links: [
{ label: "Features", url: "/features/" },
{ label: "Pricing", url: "/pricing/" },
{ label: "Customers", url: "/customers/" },
{ label: "Resources", url: "/resources/" },
{ label: "Blog", url: "/blog/" },
],
cta: { label: "Contact", url: "/contact/" },
}

links renders in the desktop nav and the mobile drawer. The cta is the highlighted button on the right of the header. Set sticky: false if you want the header to scroll away.

contactForm: {
provider: "formspree", // "formspree" | "formsubmit" | "netlify" | null
formspreeId: "your-form-id",
// formsubmitEmail: "you@example.com",
}

The Contact component reads provider and wires the form to that service. To disable the contact form entirely, set provider: null — the component renders a fallback “Email us at hello@…” instead.

ProviderWhat you setHosting requirement
formspreeformspreeId from formspree.ioAnywhere
formsubmitformsubmitEmail (your inbox)Anywhere
netlifyNothing — Netlify detects the form tagMust deploy to Netlify
analytics: {
provider: "plausible", // "google" | "plausible" | "umami" | null
plausibleDomain: "example.com",
// googleId: "G-XXXXXXXXXX",
// umamiWebsiteId, umamiScriptUrl
}

The matching <script> snippet is injected into <head> automatically. null injects nothing — perfect for development.

newsletter: {
provider: "buttondown", // "mailchimp" | "convertkit" | "buttondown" | null
heading: "Stay in the loop",
subtext: "Get product updates, tips, and insights delivered to your inbox.",
placeholder: "Enter your email",
buttonLabel: "Subscribe",
actionUrl: "https://your-provider.com/subscribe",
}

Each provider expects a different actionUrl — copy it from your provider’s “embed signup form” page.

social: {
twitter: "https://x.com/yourhandle",
github: "https://github.com/yourrepo",
linkedin: "https://linkedin.com/company/yourco",
// any key set to null is hidden
},
seo: {
title: "Aurora — The modern platform for ambitious teams",
description: "Aurora helps teams ship faster…",
ogImage: "/og.png",
twitterHandle: "@yourhandle",
},
footer: {
copyright: "© 2026 Aurora. All rights reserved.",
columns: [
{ title: "Product", links: [...] },
{ title: "Resources", links: [...] },
{ title: "Company", links: [...] },
],
}

The footer renders one column per entry. Add a fourth column and the layout expands; remove one and it contracts.

Most marketing sections — Team, Video, Timeline, Comparison, Gallery, Carousel, Tabs, AppDownload, Map — pull their copy and data from siteConfig:

team: {
heading: "Meet our team",
subtext: "The people building the future of project management.",
members: [
{ name, role, initials, image, bio },
// …
],
},
comparison: {
heading: "How Aurora compares",
subtext: "See why teams choose Aurora over the competition.",
features: [
{ name: "Real-time collaboration", aurora: true, competitorA: true, competitorB: false },
// …
],
columns: ["Aurora", "Competitor A", "Competitor B"],
},

You only need the keys for sections your theme actually uses on its pages. Unused sections can be deleted from site.ts without breaking anything — components import their config lazily.

headScripts: `<script defer src="/analytics.js"></script>`,

A raw HTML string injected into <head>. Use it for analytics pixels, verification tags, or anything your analytics provider doesn’t cover. Keep it short — for larger snippets, put them in their own .astro file and import it in src/layouts/Layout.astro instead.

siteConfig is declared as const and exported with a SiteConfig type:

export const siteConfig = { /* … */ } as const;
export type SiteConfig = typeof siteConfig;

Adding a new top-level key is free — TypeScript picks it up automatically. Renaming a key is more work: find every siteConfig.<key> reference and update it. VS Code’s “Rename Symbol” (F2) handles this cleanly.