shikidown

Installation

Packages to install, peer dependencies to satisfy, and the optional mermaid entry point.

Install

npm install shikidown shiki markdown-it @angular/elements

shiki and markdown-it are peer dependencies rather than dependencies: shikidown does not pin their versions, and you get exactly one copy of each in your bundle.

@angular/elements is listed here because ng new does not install it, even though it ships with Angular — see the note under Peer dependencies.

markdown-it v15 ships its own type definitions. On v14 they live in a separate package:

npm install --save-dev @types/markdown-it   # v14 only

Peer dependencies

PackageVersionRequired
@angular/core>=22.0.0Yes
@angular/common>=22.0.0Yes
@angular/elements>=22.0.0Yes
@angular/platform-browser>=22.0.0Yes
markdown-it>=14.0.0Yes
shiki>=4.4.3Yes
mermaid>=11.0.0Optional

@angular/core, @angular/common and @angular/platform-browser are already present in any Angular 22 application, so in practice the new installs are shiki, markdown-it and @angular/elements.

@angular/elements is what turns your components into Custom Elements, and it is required, not optional. It ships as part of Angular but ng new does not add it to package.json, so it is usually missing. provideMarkdown() imports createCustomElement from it directly — without the package the build fails on an unresolved import, before any of your own code runs.

TypeScript configuration

shiki >=4.4 declares [Symbol.dispose]() on its highlighter. That symbol is not part of the ES2022 library that ng new configures, so the build fails before it reaches your own code:

TS2550: Property 'dispose' does not exist on type 'SymbolConstructor'.

Add ESNext.Disposable to lib:

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable", "ESNext.Disposable"]
  }
}

Setting "target": "esnext" fixes it too — the implied library includes the symbol. Naming ESNext.Disposable is narrower: it enables the one missing API rather than every proposal-stage one, and it does not shift meaning when you upgrade TypeScript.

The mermaid entry point

mermaid is heavy, and most documents never contain a diagram. It is therefore an optional peer dependency reached only through a secondary entry point:

npm install mermaid
import { MermaidDirective } from 'shikidown/mermaid';

If you never import from shikidown/mermaid, mermaid never enters your dependency graph — and fenced ```mermaid blocks simply stay rendered as code. See Mermaid diagrams.

Verifying the install

app.config.ts
import { provideMarkdown } from 'shikidown';

export const appConfig: ApplicationConfig = {
  providers: [provideMarkdown()],
};
smoke-test.ts
import { MarkdownComponent } from 'shikidown';

@Component({
  imports: [MarkdownComponent],
  template: `<shikidown content="# It works" />`,
})
export class SmokeTest {}

provideMarkdown() is optional — the library falls back to its defaults when the configuration token is absent. Calling it with no arguments, as above, is equivalent to not calling it at all; it is shown here because that is where you will add options next.

Server-side rendering

shikidown is SSR-safe, with one caveat worth stating plainly: Shiki is only loaded in the browser. On the server, code fences are rendered as plain, unhighlighted <pre><code> blocks, and no Custom Element is defined. Highlighting appears once the client takes over.

Next steps

On this page