🪵 bbmd

Basic usage

This page will walk you through the basics of creating composable markdown blocks, expressing conditions, and keeping types simple. For complete documentation on bbmd's API, refer to Creating blocks.

Creating a block

In Bbmd, everything in Markdown is a block, including entire documents. You can create blocks using template literals, functional composition, or by parsing existing markdown strings.

import * as b from "bbmd"

const userName = "John"
const userAge = 30

const userDetailsDoc = b.doc(
  b.h`User details`.id("user-details"),
  b.p(b.b(userName), b.fn(`Age: ${userAge}`)),
);
import * as b from "bbmd"

const userName = "John"
const userAge = 30

const userDetailsDoc = b.md`
  # User details {#user-details}
  **${userName}**[^1]

  [^1]: Age: ${userAge}
`.parse();
import * as b from "bbmd"

const userName = "John"
const userAge = 30

const userDetailsDoc = b.md`
  ${b.h`User details`.id("user-details")}
  ${b.b(userName)}${b.fn(`Age: ${userAge}`)}
`;
# User details {#user-details}
**John**[^1]

[^1]: Age: 30

Composing blocks

Because blocks are composable, you can easily reuse them across documents and contexts. Simply create a block and then include it in other blocks as needed.

// start ignore
const userName = "John"
const userAge = 30

const userDetailsDoc = b.doc(
  b.h`User details`.id("user-details"),
  b.p(b.b(userName), b.fn(`Age: ${userAge}`)),
);
// stop ignore

const promptDoc = b.doc(
  b.h`Greeting prompt`,
  userDetailsDoc, // 👈 here's our block from earlier
  b.p`Use the above details to generate a personalized greeting message.`
).setRenderingOptions({
  enforce: { bold: { style: "__" } },
  newlineStrategy: "between_blocks",
});

// start ignore
promptDoc
// stop ignore

Notice that when embedded in promptDoc, the userDetailsDoc automatically adjusted it's heading levels and moved it's footnotes to the end of the document. Sub-blocks even respect their's hosts requesting styling.


Expressing conditions

Bbmd provides several helper methods for expressing conditions in your markdown documents. These can be used to set default values, conditionally include content, and more.

type PullRequest = { title: string; reviewer?: string; approved: boolean };

const createPrDoc = (pr: PullRequest): MarkdownDocument => {
  const reviewer = b.b(pr.reviewer)
    .default("Unknown reviewer")
    .change((block) => {
      if (pr.approved) return block.strikethrough();
      return block;
    });
  return b.doc(
    b.b(pr.title).h(),
    b.p`Reviewer: ${reviewer}`,
    b.p`Requires review`.if(!pr.approved)
  );
};

createPrDoc({ title: "100", approved: true })

Rendering a block

To render a block, simply convert it to a string however you would for other objects in Typescript.

// start ignore
const String = (block: MarkdownBlock) => block.toString();

const document = b.doc(
  b.h`Example document`.id("example-document"),
  b.p(b.b`Important text`, b.fn`example footnote`),
);
// stop ignore

String(document);
`${document}`;
document.toString();

// start ignore
document
// stop ignore

On this page