Skip to content

Core Concepts

This page aims to explain how Astro Theme Provider works at a fundamental level for theme authors and theme users.

  • If you are interested in learning about what Astro Theme Provider is and why you want to use it, you can read more here: Why?
  • If you need specific advice about how to author a theme, check out the guides under “Conventions and Techniques” in the left sidebar.
  • If you are a theme user, please refer to the User API and your theme’s documentation.

Authoring a Theme

Using Astro Theme Provider to author a theme integration is simple, but there are a few core concepts that one should know before getting started.

Project Structure

Themes are developed inside a pnpm monorepository.

  • Directory/
    • Directorypackage/
    • Directoryplayground/
    • package.json
    • pnpm-lock.yaml
    • pnpm-workspace.yaml
    • tsconfig.json

The theme integration itself lives inside a package with a structure similar to a normal Astro project:

  • Directorypackage/
    • Directorypublic/
    • Directorysrc/
      • Directoryassets/
      • Directorycomponents/
      • Directorylayouts/
      • Directorypages/
      • Directorystyles/
    • index.ts
    • package.json
    • README.md

Theme integrations must be authored alongside a playground. The playground is responsible for generating types during development and enables previewing changes made to the theme.

  • Directoryplayground/
    • Directorypublic/
    • Directorysrc/
    • astro.config.mjs
    • package.json
    • tsconfig.json

Theme Configuration

Theme authors define the shape for the theme user configuration using a Zod schema.

package/index.ts
import defineTheme from 'astro-theme-provider';
import { z } from 'astro/zod';
export default defineTheme({
name: 'my-theme',
schema: z.object({
title: z.string(),
description: z.string().optional()
})
})

Important: The theme config object must be JSON serializable. Values like functions and classes do not work inside theme configurations.

Pages and components can then be designed so that theme users can easily customize them without touching core parts of the theme.

package/src/pages/index.astro
---
import config from 'my-theme:config'
---
<html>
<head>
<title>{config.title}</title>
</head>
<body>
<h1>{config.title}</h1>
<p>{config.description}</p>
</body>
</html>

Theme Context

Theme integrations are dynamic, they may be used inside a variety of projects with different combinations of user configurations and enviroments. To account for this, themes have built-in utilities to access information about the context the theme is running in. For example:

---
import { pages, integrations } from 'my-theme:context';
---
{ integrations.has('@astrojs/rss') &&
<a href="/rss.xml"></a>
}
{ pages.get('/blog') &&
<a href={pages.get('/blog')}></a>
}

This allows theme authors to create dynamic themes that adapt to a user’s project and configuration.

Virtual Modules

Theme authors should load components and assets as virtual modules. This enables complete customization for a theme user via overrides.

  • Directorypackage/
    • Directorysrc/
      • Directoryassets/
        • logo.png
      • Directorycomponents/
        • Hero.astro
      • Directorylayouts/
        • Layout.astro
      • Directorystyles/
        • global.css
package/src/pages/index.astro
---
import { Layout } from 'my-theme:layouts';
import { Hero } from 'my-theme:components';
import { logo } from 'my-theme:assets';
import "my-theme:styles";
---
<Layout>
<Hero image={logo} />
</Layout>

Using a Theme

Once a theme integration has been authored and published to NPM, a user can add it to their project with a single command:

Terminal window
pnpm astro add my-theme

This will install the theme package and add it to the integrations array inside the Astro config:

astro.config.mjs
import { defineConfig } from 'astro/config';
import myTheme from 'my-theme';
export default defineConfig({
integrations: [
myTheme({
// ...
}),
],
});

Configuring a theme

Users can configure a theme using the config options that were defined by the theme author:

myTheme({
config: {
title: 'My Site!',
description: 'My Astro Theme Provider Site!'
}
})

Overriding part of a theme

For complete customization, various parts of a theme can be overriden by a theme user. For example:

Overriding routes:

myTheme({
pages: {
// Toggling routes off
"/404": false,
// Renaming injected routes
"/blog": "/posts",
}
})

Overriding styles:

myTheme({
overrides: {
// Appending new styles
styles: [
'./src/styles/override.css'
]
}
})

Overriding assets:

myTheme({
overrides: {
// Changing an image
assets: {
logo: './src/assets/custom-logo.png'
}
}
})

Disabling integrations:

myTheme({
integrations: {
// Remove injected integration
"@astrojs/sitemap": false
}
})