Sites

Site-scoped settings live in either site (singular, one site) or sites (plural map, multiple sites). Each entry is a SiteConfig with the same set of fields.

Single-site

Most projects have one site:

{
  "$schema": "https://refrakt.md/refrakt.config.schema.json",
  "site": {
    "contentDir": "./content",
    "theme": "@refrakt-md/lumina",
    "target": "svelte"
  }
}

The legacy flat shape is equivalent — both produce sites.default after normalization, and adapters like the SvelteKit plugin pick the lone site automatically.

Multi-site

Multi-site repos declare named entries under sites:

{
  "$schema": "https://refrakt.md/refrakt.config.schema.json",
  "sites": {
    "main": {
      "contentDir": "./site/content",
      "theme": "@refrakt-md/lumina",
      "target": "svelte",
      "baseUrl": "https://example.com"
    },
    "blog": {
      "contentDir": "./blog/content",
      "theme": "@refrakt-md/lumina",
      "target": "svelte",
      "baseUrl": "https://blog.example.com"
    }
  }
}

Each adapter (SvelteKit, Astro, Nuxt, Next, Eleventy) accepts a site option that selects which entry to build:

// site/vite.config.ts (SvelteKit)
import { sveltekit } from '@sveltejs/kit/vite';
import { refrakt } from '@refrakt-md/sveltekit';

export default defineConfig({
  plugins: [
    sveltekit(),
    refrakt({ configPath: '../refrakt.config.json', site: 'main' }),
  ],
});

Single-site projects can omit the site option — the plugin picks the only entry automatically. Multi-site projects must pass it explicitly; the plugin throws at config-load time with the available names if it's missing.

CLI --site flag

Site-scoped commands (inspect, contracts, validate, scaffold-css, package validate) accept --site <name>:

# Pick a specific site explicitly
npx refrakt inspect hint --type=warning --site main

# Single-site projects don't need it
npx refrakt inspect hint --type=warning

Unknown site names produce a "did you mean?" suggestion. Plan-only repos (no sites declared) error with a "no site configured" message when site-scoped commands are run.

SiteConfig fields

A SiteConfig accepts these fields. Required fields are bold.

Core (required)

FieldTypeDescription
contentDirstringPath to the content directory, relative to the project root.
themestringActive theme — npm package name (e.g., @refrakt-md/lumina) or relative path.
targetstringTarget adapter identifier (svelte, astro, next, nuxt, eleventy, html).

SEO and branding

FieldTypeDescription
baseUrlstringPublic base URL of the site. Used for canonical links, og:url, og:image.
siteNamestringHuman-readable site name for og:site_name and Organization JSON-LD.
logostringSite logo path used in Organization JSON-LD (e.g., /favicon-192.png).
defaultImagestringDefault og:image for pages without their own (recommended 1200x630).

Content rendering

FieldTypeDescription
packagesstring[]Community rune packages to merge into this site's ThemeConfig.
routeRulesRouteRule[]Route-to-layout mapping rules (first match wins).
overridesRecord<string, string>Component overrides — typeof name → relative component path.
runesRunesConfigRune resolution: prefer, aliases, local.
highlightHighlightConfigSyntax highlight theme — single name or { light, dark }.
iconsRecord<string, string>Custom icon SVGs merged into the theme's global icon group.
tintsRecord<string, object>Project-level tint presets.
backgroundsRecord<string, object>Project-level background presets.
sandboxobject{ examplesDir } — directory for {% sandbox %} examples.

Example: full site

{
  "site": {
    "contentDir": "./content",
    "theme": "@refrakt-md/lumina",
    "target": "svelte",
    "baseUrl": "https://example.com",
    "siteName": "My Documentation",
    "logo": "/favicon-192.png",
    "packages": ["@refrakt-md/marketing", "@refrakt-md/docs"],
    "routeRules": [
      { "pattern": "blog/**", "layout": "blogArticleLayout" },
      { "pattern": "docs/**", "layout": "docsLayout" },
      { "pattern": "**", "layout": "defaultLayout" }
    ],
    "highlight": { "theme": { "light": "github-light", "dark": "github-dark" } },
    "runes": {
      "prefer": { "storyboard": "@refrakt-md/marketing" },
      "aliases": { "callout": "hint" }
    }
  }
}

Path resolution

Inside a site entry, paths like contentDir: "./content" are interpreted relative to the vite app's resolved root, not the config file location. So a config at the repo root with site.contentDir: "./content" and a SvelteKit plugin running from site/ resolves content from site/content/. This matters when you split your refrakt.config.json across multiple sites with different vite roots.