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 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; the plugin is built for
TypeDoc 0.28. @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', };
plugin: [load]passes the plugin itself, not its package name. TypeDoc imports plugins named by a string from its own installation, where package managers such as pnpm hide your project's dependencies, so passingloadworks with any package manager.docKitsets where the reference goes. With it set, TypeDoc skips its own HTML output.basePathmakes the source links relative to your repository's root, so they match your files on GitHub.
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
- A page per exported class, interface, enum, type alias, function and
variable, in
classes/,interfaces/,enums/,types/,functions/andvariables/. Namespaces, and modules when you have several entry points, get a page inmodules/. type-map.jsonmaps each type name to its page, so doc-kit can link type annotations to it.pages.jsonlists the pages, for your site's navigation.
There's no page for the project itself: that one is yours to write, for
example as docs/index.md.
The plugin writes into docs/api/ without emptying it first. Delete the
folder before generating when you rename or remove exports, so their old
pages don't stay behind. Since it's generated, you can also leave it out of
version control.
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, so type links need a host that serves
/api/interfaces/Kitten from Kitten.html, as GitHub Pages, Netlify and
Cloudflare do, and Vercel with cleanUrls. See
Page URLs.
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.