Tangly v0.2 ships richer code blocks, page chrome, and more — see what's new

Schema

Every top-level field in docs.json. Colors, navigation, logo, navbar, footer, redirects, SEO, analytics, API, themes, fonts, and more.

~ 6 min read

docs.json is the source of truth for everything that isn’t page content: branding, navigation, integrations, behavior. The full schema is defined in packages/schema/src/docs-json.ts.

For per-page metadata, see Frontmatter.

Skeleton

json
{
  "$schema": "https://tangly.dev/schema/docs.json",
  "name": "My Docs",
  "theme": "tang",
  "colors": { "primary": "#EA580C" },
  "logo": { "light": "/logo-light.svg", "dark": "/logo-dark.svg" },
  "favicon": "/favicon.ico",
  "navigation": {
    "groups": [
      { "group": "Getting started", "pages": ["introduction", "quickstart"] }
    ]
  }
}

Only name and navigation are required. Everything else has a sensible default.

$schema

Type: string (URL). Default: none.

Editor autocomplete + validation. Set to https://tangly.dev/schema/docs.json so VS Code, Cursor, etc. surface field hints inline.

name

Type: string (required). Site name. Renders in the topnav and <title>.

description

Type: string. Site-wide description. Used as the default OpenGraph description.

theme

Type: "tang" | "pith" | "pip" | "readable" | "geist" | string. Default: "tang".

Pick a built-in theme. Unknown values fall back to tang. Tangly stays tolerant so projects mid-migration don’t fail validation. Legacy aliases (mint/maple/palm/willow/linden/almond/aspen/luma/sequoia) all resolve to tang.

json
{ "theme": "pith" }

See Themes for screenshots and tradeoffs.

colors

Override the theme palette. Five tokens.

FieldTypeDescription
primaryhexPrimary brand color: buttons, links, accents
lighthexLighter shade for highlights
darkhexDarker shade for hovers / active states
backgroundhex | { light, dark }Page background. Object form splits light/dark mode.
anchorshex | { from, to }Anchor accent. Object form is a 135° linear gradient.
json
{
  "colors": {
    "primary": "#EA580C",
    "light": "#F97316",
    "dark": "#C2410C",
    "background": { "light": "#FAFAF7", "dark": "#0B0B0F" },
    "anchors": { "from": "#F97316", "to": "#C2410C" }
  }
}

Hex must be 3, 6, or 8 hex digits with a leading #.

Type: string | { light?, dark?, href? }. Site logo.

json
{ "logo": "/logo.svg" }
json
{
  "logo": {
    "light": "/logo-light.svg",
    "dark": "/logo-dark.svg",
    "href": "https://example.com"
  }
}

href overrides the default link target (/). Useful when the docs site is part of a larger landing page.

favicon

Type: string | { light, dark }. Path under your project root.

json
{ "favicon": "/favicon.ico" }

Light/dark form is rare. Most browsers ignore it.

The nav tree. Three forms; pick one or combine. Schema in packages/schema/src/navigation.ts.

json
{
  "navigation": {
    "pages": ["introduction", "quickstart"],
    "groups": [
      { "group": "Guides", "pages": ["guides/billing", "guides/auth"] }
    ],
    "tabs": [
      {
        "tab": "Reference",
        "groups": [
          { "group": "API", "pages": ["reference/api/users"] }
        ]
      }
    ],
    "anchors": [{ "anchor": "Community", "href": "https://discord.gg/x" }],
    "dropdowns": [{ "dropdown": "Resources", "pages": [] }],
    "versions": [
      { "version": "v2", "default": true, "groups": [] },
      { "version": "v1", "groups": [] }
    ],
    "languages": [{ "language": "en", "groups": [] }],
    "global": {
      "anchors": [{ "anchor": "GitHub", "href": "https://github.com/tanglydocs/tangly" }]
    }
  }
}

Each node type:

NodeRequired keysDescription
Page(string)Page slug: relative path without .mdx
Groupgroup, pagesSidebar group
TabtabTop-level nav tab. Children: pages | groups | anchors
Anchoranchor, hrefExternal link in nav
DropdowndropdownGroup of sub-links shown via dropdown
VersionversionVersion selector. Children scoped to that version
LanguagelanguageLanguage selector. Children scoped to that locale

global lists anchors/dropdowns/tabs that should appear on every variant (every version, every language).

Top navigation bar.

FieldTypeDescription
links{ label, href, icon? }[]Inline links in the navbar
primary{ type: "button" | "github", label?, href }Primary CTA: either a labeled button or a GitHub-icon button
json
{
  "navbar": {
    "links": [{ "label": "Pricing", "href": "/pricing" }],
    "primary": { "type": "button", "label": "Sign up", "href": "https://app.example.com" }
  }
}
json
{
  "navbar": {
    "primary": { "type": "github", "href": "https://github.com/tanglydocs/tangly" }
  }
}
FieldTypeDescription
socialsRecord<string, string>Map of icon name → URL. e.g. { "x": "https://x.com/...", "github": "https://github.com/..." }
links{ header?, items: NavbarLink[] }[]Grouped link columns
lastUpdatedbooleanShow last-updated timestamp on every page footer
editUrlstringEdit-this-page URL template. {path} substitutes the page file’s path.
repostringRepository URL. Derives editUrl if not set explicitly. Falls back to git remote get-url origin.
json
{
  "footer": {
    "socials": {
      "github": "https://github.com/tanglydocs/tangly",
      "x": "https://x.com/tanglydocs"
    },
    "links": [
      {
        "header": "Resources",
        "items": [
          { "label": "Blog", "href": "https://example.com/blog" },
          { "label": "Status", "href": "https://status.example.com" }
        ]
      }
    ],
    "lastUpdated": true,
    "editUrl": "https://github.com/owner/repo/edit/main/{path}"
  }
}

redirects

Permanent or temporary URL redirects. Used by every adapter.

json
{
  "redirects": [
    { "source": "/old-path", "destination": "/new-path", "permanent": true },
    { "source": "/api/v1/:slug", "destination": "/api/v2/:slug" }
  ]
}

seo

FieldTypeDescription
metatagsRecord<string, string>Extra <meta> tags on every page
indexing"all" | "navigable""navigable" excludes pages not in nav from search engines
json
{
  "seo": {
    "metatags": { "robots": "index,follow", "twitter:site": "@tanglydocs" },
    "indexing": "navigable"
  }
}

analytics

Wire up an analytics provider. Tangly ships providers for posthog, plausible, fathom, ga4, gtm, amplitude, hotjar, mixpanel, segment, pirsch, logrocket, heap.

json
{
  "analytics": {
    "posthog": { "apiKey": "phc_…", "apiHost": "https://app.posthog.com" }
  }
}
json
{ "analytics": { "plausible": { "domain": "docs.example.com" } } }
json
{ "analytics": { "ga4": { "measurementId": "G-XXXXXX" } } }

You can configure multiple providers at once; they all fire.

api

OpenAPI / AsyncAPI integration.

FieldTypeDescription
baseUrlstring | string[]Default base URL(s) for try-it-out. Multiple → user picks
auth.method"bearer" | "basic" | "key" | "none"Auth scheme for the try-it-out form
auth.namestringHeader name when method="key"
playground.mode"interactive" | "simple" | "hide"Try-it form behavior
playground.proxybooleanSend through a server-side CORS proxy
openapistring | string[]Path(s) to OpenAPI spec(s); auto-generates pages
viewer"tangly" | "scalar" | "redoc" | "stoplight"Default viewer for OpenAPI pages
asyncapistring | string[]Path(s) to AsyncAPI spec(s)
mdx.serverstring | string[]MCP-style server URL(s) for AI agents
json
{
  "api": {
    "baseUrl": "https://api.example.com",
    "auth": { "method": "bearer" },
    "playground": { "mode": "interactive" },
    "openapi": "/openapi.json",
    "viewer": "tangly"
  }
}

appearance

FieldTypeDescription
default"light" | "dark" | "system"Default color mode
strictbooleanHide the theme toggle (force default)
readingTimebooleanShow estimated reading time in page header
readingProgressbooleanRender a 2px scroll-progress bar across the top
json
{
  "appearance": {
    "default": "system",
    "strict": false,
    "readingTime": true,
    "readingProgress": true
  }
}

code

FieldTypeDescription
copyButtonbooleanShow copy button on every code block (default true)
themestring | { light, dark }Shiki theme. Default: vitesse-light / vitesse-dark.
json
{
  "code": {
    "copyButton": true,
    "theme": { "light": "vitesse-light", "dark": "vitesse-dark" }
  }
}

background

Site-wide background.

FieldTypeDescription
imagestringPath to background image
color{ light?, dark? }Per-mode background colors (hex)
decoration"gradient" | "grid" | "windows"Subtle decorative pattern
json
{
  "background": {
    "color": { "light": "#FAFAF7", "dark": "#0B0B0F" },
    "decoration": "grid"
  }
}

fonts

Override heading and/or body fonts.

FieldTypeDescription
heading{ family, weight?, source?, format? }Heading font face
body{ family, weight?, source?, format? }Body font face
json
{
  "fonts": {
    "heading": {
      "family": "Inter",
      "weight": "400 700",
      "source": "https://rsms.me/inter/font-files/InterVariable.woff2",
      "format": "woff2-variations"
    }
  }
}

styling

Subtle layout flags.

FieldTypeDescription
eyebrows"section" | "breadcrumbs"Heading eyebrow style
codeblocks"system" | "dark"Force code blocks to always use dark theme regardless of page mode

integrations

Free-form bag for third-party integrations not listed above. Schema is Record<string, unknown>. Tangly itself reads only what it knows about.

errors

FieldTypeDescription
404.redirectbooleanRedirect to the closest navigable page on 404. Default false (show the 404 template instead)
json
{ "errors": { "404": { "redirect": true } } }

You can also override the 404 page entirely by dropping a 404.mdx at your project root.

contextual

Reader-initiated actions exposed in the per-page Copy page menu (top-right of every doc page). All actions reference the page’s Markdown twin at /<slug>.md.

json
{ "contextual": { "options": ["copy", "copy-url", "view", "chatgpt", "claude"] } }
ActionEffect
copyCopy the page’s raw Markdown to the clipboard
copy-urlCopy the absolute URL of the Markdown twin
viewOpen the Markdown twin in a new tab
chatgptOpen ChatGPT prefilled with the Markdown URL
claudeOpen Claude prefilled with the Markdown URL

Omitting contextual shows all five actions. Set options to [] to hide the menu entirely. List a subset to show only those actions.

See Copy page menu for screenshots and the full UX.

FieldTypeDescription
promptstringPlaceholder text in the search input
json
{ "search": { "prompt": "Search the docs…" } }

thumbnails

Controls auto-generated social cards (the Open Graph / Twitter preview image shown when a page is shared). Tangly renders a branded 1200×630 card per page from the page title, description, the owning tab, your theme, and your colors / logo. Cards generate by default once siteUrl is set (an absolute URL is required for og:image).

FieldTypeDescription
enabledbooleanMaster switch. Default true. Set false to turn off generation.
backgroundstringCard background: a hex color (e.g. "#0B0B0F"). Defaults to the theme surface.
accentstringAccent color (hex). Defaults to colors.primary.
imagestringOne static image used as og:image for every page, instead of generated cards.
json
{ "thumbnails": { "accent": "#EA580C" } }

Cards are prerendered to /og/<slug>.png at build (and rendered on demand in tangly dev for live preview). Per-page overrides: set seo.ogImage in a page’s frontmatter. Drafts and noindex pages get no card.

metadata

Free-form Record<string, string>. Rendered as extra <meta> tags on every page. Use for application-name, apple-mobile-web-app-title, etc.

A site-wide announcement bar above the topnav.

FieldTypeDescription
contentstringMDX-flavored body
dismissiblebooleanUser can close it
idstringStable ID, used as the localStorage dismissal key. Change the id to re-show after a previous dismiss.
type"info" | "warning" | "success"Color tone
json
{
  "banner": {
    "content": "🎉 Tangly v1.0 is out — [read the announcement](/blog/v1).",
    "dismissible": true,
    "id": "v1-launch",
    "type": "success"
  }
}

icons

Pick the icon library for <Icon icon="..." /> and Mintlify-aliased prop strings.

FieldTypeDescription
library"lucide" | "fontawesome"Default icon library. Tangly ships with Lucide.
json
{ "icons": { "library": "lucide" } }

Source

Last updated Edit this page

Type to search…

↑↓ navigate open esc close