If your API is written in TypeScript, or in JavaScript with JSDoc comments,
its reference can come from the source. TypeDoc reads
your code and its doc comments, the @doc-kit/typedoc plugin writes them as
doc-kit Markdown (a page per export, with signatures, typed lists, stability
and source links), and doc-kit builds those pages into one site with your
hand-written guides.
@doc-kit/typedoc is experimental: its output and options can still change.
It takes two commands: npx typedoc writes the reference into docs/api/,
then npx @doc-kit/cli generate builds everything in docs/.
npm install --save-dev typedoc@0.28 @doc-kit/typedoc @doc-kit/cli @doc-kit/generator-react
typedoc and @doc-kit/typedoc write the reference, and @doc-kit/cli and
@doc-kit/generator-react build the site, as in
Getting started.
Create typedoc.config.mjs next to your package.json:
import { load } from '@doc-kit/typedoc'; export default { plugin: [load], entryPoints: ['src/index.ts'], // Source links are relative to it: the root of your repository basePath: '.', docKit: 'docs/api', };
The @doc-kit/typedoc reference explains why
the plugin is passed as load, and its Output
section why basePath is your repository's root. Everything else, such as
entry points, the tsconfig and which exports to include, is configured with
TypeDoc's own options.
npx typedoc
TypeDoc finds typedoc.config.mjs in the working directory, and the plugin
writes the reference in TypeDoc's folder layout:
docs/api/ ├── functions/ │ └── adopt.md ├── interfaces/ │ └── Kitten.md ├── pages.json └── type-map.json
The @doc-kit/typedoc reference describes
each file.
Take this source file:
/** A kitten, adopted or not. */ export interface Kitten { /** The kitten's name. */ name: string; } /** * Adopts a kitten, once the paperwork is done. * * @param name - The kitten's name. * @param options - How to adopt it. * @param options.color - Coat color. * @param options.signal - Cancels the adoption. * @returns The adopted kitten. * @experimental */ export async function adopt( name: string, options: { color?: string; signal?: AbortSignal } = {} ): Promise<Kitten> { return { name }; }
The plugin writes adopt() as this page:
# `adopt(name[, options])` <!-- YAML title: "Function: adopt()" source_link: src/index.ts#L17 --> > [Stability: 1](documentation.html#stability-index) - Experimental - `name` {string} The kitten's name. - `options` {Object} How to adopt it. **Default:** `{}`. - `color` {string} Coat color. - `signal` {AbortSignal} Cancels the adoption. - Returns: {Promise<Kitten>} The adopted kitten. Adopts a kitten, once the paperwork is done.
It's the Markdown you would write by hand, as described in Writing documentation:
- The heading is the call, with
optionsin brackets: it has a default value, so it's optional. - The YAML comment holds the page title and the link to the source.
@experimentalbecomes a stability indicator.- The typed list holds each parameter's type and
@paramdescription, the properties ofoptionsnested under it, and the return value with its@returnsdescription. - The summary follows, as the entry's description.
Once doc-kit builds it, Promise<Kitten> links to the Kitten page through
the type map, and Promise and AbortSignal link to MDN.
Other tags follow the same pattern: @deprecated becomes a
Stability: 0 - Deprecated indicator with its message, @example an
"Example" section, and {@link} a link to its target. TypeDoc's @remarks
and @since aren't rendered, so keep that text in the summary.
Declarations follow doc-kit's conventions too: a class gets a Class:
heading, and a property an entry with a Type: item, named after its owner
with a lowercase first letter, such as kitten.name. The
@doc-kit/typedoc reference
lists every tag and declaration the plugin renders.
Point doc-kit at the whole docs/ folder, and at the type map:
export default { target: ['html', 'orama-db'], global: { project: 'Kittens', repository: 'you/kittens', input: ['docs/**/*.md'], output: 'out', }, metadata: { typeMap: 'docs/api/type-map.json', }, };
inputreads the guides and the reference alike:docs/**/*.mdincludesdocs/api/.metadata.typeMaplinks every type annotation naming one of your types to its page, in the reference and in your guides.repository(owner/name) turns the source links into links to your files on GitHub.
The type map's paths are relative to the folder your input glob starts from,
here docs/:
{ "Kitten": "api/interfaces/Kitten" }
These paths have no extension; see The type map for what that asks of your host.
If the reference sits deeper, such as in docs/reference/api/, set the
plugin's docKitBasePath option to 'reference/api' so the paths still
match.
In your guides, link to reference pages by their file, and name your types in braces, as you would anywhere else:
# Kittens Call [`adopt()`](./api/functions/adopt.md) to bring home a {Kitten}.
Run TypeDoc before each build, for example from a script in your
package.json:
{ "scripts": { "docs": "typedoc && doc-kit generate" } }
npm run docs then builds the site into out/; preview it as in
Getting started.
Without navigation.sidebar, the site's
sidebar lists every page in one "API Documentation" group. pages.json gives
you what you need to build your own: the name, kind, URL and category of each
reference page.
[ { "name": "Kitten", "kind": "Interface", "url": "/api/interfaces/Kitten" }, { "name": "adopt", "kind": "Function", "url": "/api/functions/adopt" } ]
TypeDoc categorizes the exports of each module and namespace separately: once
one of them has a @category, TypeDoc files the others under "Other" (its
defaultCategory option). When none has one, their pages have no category,
as above.
Read the page list in your configuration, and turn it into sidebar groups. This one makes a group per category, next to your guides:
import { readFileSync } from 'node:fs'; const pages = JSON.parse( readFileSync(new URL('./docs/api/pages.json', import.meta.url), 'utf8') ); // A group per category, and "Other" for the pages without one const categories = Object.groupBy(pages, ({ category }) => category ?? 'Other'); export default { // ... html: { navigation: { sidebar: [ { groupName: 'Guides', items: [{ label: 'Introduction', link: '/index' }], }, ...Object.entries(categories).map(([groupName, entries]) => ({ groupName, items: entries.map(({ name, url }) => ({ label: `\`${name}\``, link: url, })), })), ], }, }, };
The configuration reads pages.json when it loads, so run TypeDoc first.
Customizing the site covers the rest of the
navigation.
TypeDoc reads JavaScript too, taking the types from its JSDoc comments, once
TypeScript is allowed to: set allowJs (or checkJs) in your
tsconfig.json, and point entryPoints at your JavaScript, such as
src/index.js.
{ "compilerOptions": { "allowJs": true }, "include": ["src"] }
The types then come from the JSDoc tags:
/** * Adopts a kitten, once the paperwork is done. * * @param {string} name The kitten's name. * @param {object} [options] How to adopt it. * @param {string} [options.color] Coat color. * @param {AbortSignal} [options.signal] Cancels the adoption. * @returns {Promise<{ name: string }>} The adopted kitten. * @experimental */ export async function adopt(name, options = {}) { return { name }; }
This gives the same page as the TypeScript version, apart from the return
type, which reads as written: {Promise<{ name: string }>}. A @typedef of
an object type gets a page of its own, as an interface would.
The @doc-kit/typedoc reference lists the
plugin's other options: docKitMemberPages gives each member of a large
options type a page of its own, docKitReceivers changes the name members
are documented on, and docKitUrlAdapter keeps the URLs of an earlier site
working. To shape the rest of the site, continue with
Customizing the site.