> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Bazel Starlark Docs on the Registry

> Explore Bazel Starlark API docs on the Registry for updated ruleset documentation, publishing guides, and community contribution opportunities

export const BlogPost = ({title, date, authors, tags, image, children}) => {
  const tagList = tags ? tags.split(", ").filter(Boolean) : [];
  const tagSlug = t => t.toLowerCase().replace(/&/g, "").replace(/\+/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
  const formattedDate = date ? new Date(date + "T00:00:00").toLocaleDateString("en-US", {
    year: "numeric",
    month: "long",
    day: "numeric"
  }) : "";
  return <section className="w-full flex justify-center px-4 py-12 md:py-16">
      <div style={{
    maxWidth: "800px",
    width: "100%"
  }}>
        {image && (typeof image === "string" ? <img noZoom src={image} alt={title} className="w-full rounded-xl mb-8" style={{
    maxHeight: "400px",
    objectFit: "cover"
  }} /> : <div className="blog-post-hero-image">{image}</div>)}
        <h1 className="text-3xl md:text-4xl font-bold text-zinc-900 dark:text-white">
          {title}
        </h1>
        <div className="flex flex-wrap items-center gap-3 mt-4 text-sm text-zinc-500 dark:text-zinc-400">
          {authors && <span>{authors}</span>}
          {authors && formattedDate && <span>·</span>}
          {formattedDate && <span>{formattedDate}</span>}
        </div>
        {tagList.length > 0 && <div className="flex flex-wrap gap-2 mt-3">
            {tagList.map(tag => <a key={tag} href={"/blog/tags/" + tagSlug(tag)} className="px-2 py-0.5 rounded-full text-xs bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 hover:bg-blue-100 dark:hover:bg-blue-900/40 hover:text-blue-700 dark:hover:text-blue-300 transition">
                {tag}
              </a>)}
          </div>}
        <hr className="my-8 border-zinc-200 dark:border-zinc-700" />
        <div className="prose dark:prose-invert max-w-none">{children}</div>
      </div>
    </section>;
};

export const MarketingPage = () => <div className="marketing-page-marker" style={{
  display: "none"
}} />;

export const Section = ({children, className = "", gray = false, dark = false, id}) => <section id={id} className={`w-full flex justify-center px-4 py-16 md:py-24 ${gray ? "bg-gray-50 dark:bg-zinc-900" : dark ? "bg-zinc-900 dark:bg-zinc-950" : ""} ${className}`}>
    <div className="w-full" style={{
  maxWidth: "1140px"
}}>
      {children}
    </div>
  </section>;

<MarketingPage />

<BlogPost title="Bazel Starlark Docs on the Registry" date="2025-10-01" authors="Alex Eagle" tags="Gazelle, Bazel">
  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-3f7edbd41c.png?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=f73ce6093be6e409e453c1d424152dd5" alt="" className="blog-post-cover" width="1893" height="1019" data-path="images/blog/hashnode/hn-3f7edbd41c.png" />

  About a year ago, I experimented with buf and starlark docgen. I didn’t make any public announcement at the time. This past year I’ve been continuing to evolve the design, and I’m excited to have finally launched it!

  For example here is [https://registry.bazel.build/modules/tar.bzl](https://registry.bazel.build/modules/tar.bzl):

  <img noZoom src="https://mintcdn.com/aspectbuild/x1L7Iep716jCyJVo/images/blog/hashnode/hn-cb0a8da296.png?fit=max&auto=format&n=x1L7Iep716jCyJVo&q=85&s=802f36173e88091b756863a7a4fb04fc" alt="" className="block mx-auto" width="1370" height="1319" data-path="images/blog/hashnode/hn-cb0a8da296.png" />

  ## Documenting Bazel APIs

  Some documentation for the Bazel “core” appears on [https://bazel.build](https://bazel.build). However Bazel is extended by rulesets which are published as modules. How do developers find the API documentation for these?

  The answer has been quite fragmented, with the community taking several approaches:

  1. Markdown or ReStructured Text files checked into the repo like [https://github.com/bazel-contrib/rules\_go/tree/master/docs/go/core](https://github.com/bazel-contrib/rules_go/tree/master/docs/go/core). These rely on some testing that the checked-in files match the stardoc output. So a `stardoc_with_diff_test` is commonly included, like [this one for the Go example](https://github.com/bazel-contrib/rules_go/blob/master/docs/doc_helpers.bzl). This is not great for contributors who submit a pull request (PR) with a minor correction to some Starlark code — they are met with a red PR and have to update the codegen. Worse, that process uses a Java program in the stardoc module which has to be built from source. This has a bunch of dependencies like protoc which also builds from source. It can take 10min to update the codegen following a 10sec minor correction. This leads to contributors abandoning the fix.

  2. Using GitHub Pages like [https://bazelbuild.github.io/rules\_rust/](https://bazelbuild.github.io/rules_rust/) or adding a versioning scheme like [https://bazelbuild.github.io/rules\_pkg/](https://bazelbuild.github.io/rules_pkg/)

  3. Using Sphinx and a [custom Bazel publishing pipeline](https://rules-python.readthedocs.io/en/latest/sphinxdocs/sphinx-bzl.html) like [https://rules-python.readthedocs.io/en/latest/](https://rules-python.readthedocs.io/en/latest/)

  4. Aspect had a legacy docsite where we rendered API docs.

  Most of these were not great, missing features like versioning, full-text search, a button to copy a code sample, or nav-to-edit.

  And most importantly, there was no one place to look. As a developer, if you don’t know which Bazel Module contains the doc you need, you end up searching around all these places. Bazel Modules are commonly published on the Bazel Central Registry (BCR) at [https://registry.bazel.build](https://registry.bazel.build). So, that’s where I added them!

  ## How it Works

  ### Generating the Docs

  Behind the scenes, Bazel has a [built-in rule `starlark_doc_extract`](https://bazel.build/versions/8.3.0/reference/be/general#starlark_doc_extract), in the Java core code, which runs Bazel’s Starlark interpreter over a given Starlark file. The interpreter is required because `.bzl` files use a standard library which is Bazel-specific and not part of the Starlark language spec, and the documentation needs to be aware of that. It also needs to read from transitively-loaded files in the `deps` of a `bzl_library` target.

  As a Starlark author, you don’t really want to think about generating the API docs, it should just work! So writing `bzl_library` in your BUILD files is sufficient - and there’s a Bazel Gazelle extension to write those for you.

  The implementation of `bzl_library` in bazel-skylib doesn’t actually do any documentation generation, or even [validation of the inputs](https://github.com/bazelbuild/bazel-skylib/issues/568).

  So we have an improved one in bazel-lib. Here’s my first opportunity to link you to the documentation on the BCR: [https://registry.bazel.build/modules/bazel\_lib#-bzl\_library-bzl](https://registry.bazel.build/modules/bazel_lib#-bzl_library-bzl)

  At this point, a rule author can `bazel query ‘kind(starlark_doc_extract, //...)’` to see what APIs have their documentation available.

  ### Publishing the Docs

  We want to include these docs in the releases, next to the artifact users download. Most rules use the [Publish-to-BCR](https://github.com/bazel-contrib/publish-to-bcr) workflow or app which require a `release_prep.sh` script, so we can just add a snippet there:

  ```bash theme={null}
  # Add generated API docs to the release
  # See https://github.com/bazelbuild/bazel-central-registry/blob/main/docs/stardoc.md
  docs="$(mktemp -d)"; targets="$(mktemp)"
  bazel --output_base="$docs" query --output=label --output_file="$targets" 'kind("starlark_doc_extract rule", //...)'
  bazel --output_base="$docs" build --target_pattern_file="$targets"
  tar --create --auto-compress \
      --directory "$(bazel --output_base="$docs" info bazel-bin)" \
      --file "$GITHUB_WORKSPACE/${ARCHIVE%.tar.gz}.docs.tar.gz" .
  ```

  > Note: the `--output_file` flag was added in Bazel 7.5.0

  Next, we want the module's metadata to point to that docs.tar.gz file, by editing the `source.template.json` to include \`"docs\_url": "[https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/{REPO}-{TAG}.docs.tar.gz](https://github.com/%7BOWNER%7D/%7BREPO%7D/releases/download/%7BTAG%7D/%7BREPO%7D-%7BTAG%7D.docs.tar.gz)",\`

  > Note: you need publish-to-bcr version v0.2.3 or greater to pick up [a fix](https://github.com/bazel-contrib/publish-to-bcr/pull/290) for multiple replacements in source.template.json

  Now the module just gets published as usual. The docs\_url property points to an archive of all the stardocs in their binaryproto format.

  ### Rendering the Docs

  Google maintains [https://github.com/bazelbuild/stardoc](https://github.com/bazelbuild/stardoc) which is a Java implementation of a Velocity template renderer for stardoc binaryprotos. However the BCR UI is a TypeScript Next.js application. We don’t want to introduce a Java source dependency. Not only that — but look at the “Legacy WORKSPACE setup” on [https://github.com/bazelbuild/stardoc/releases/tag/0.8.0](https://github.com/bazelbuild/stardoc/releases/tag/0.8.0) for an idea of the mass of transitive dependencies it requires at runtime.

  As I pointed out in that blog post I linked at the start, we can just use the `@buf/bazel_bazel.bufbuild_es` NPM package to parse the binaryproto’s, so we get a short implementation of data fetching: [https://github.com/bazel-contrib/bcr-ui/blob/main/data/stardoc.ts](https://github.com/bazel-contrib/bcr-ui/blob/main/data/stardoc.ts)

  Now that we have the Stardocs in a TypeScript object, we just need a standard React component to render them: [https://github.com/bazel-contrib/bcr-ui/blob/main/components/Stardoc.tsx](https://github.com/bazel-contrib/bcr-ui/blob/main/components/Stardoc.tsx) — this one is a little longer, but it’s mostly written and maintained by AI.

  ## Come Use It and Contribute!

  The BCR-UI project is governed by the Rules Authors SIG under Linux Foundation: [https://github.com/bazel-contrib/bcr-ui](https://github.com/bazel-contrib/bcr-ui)

  Your help is greatly appreciated!

  * Update a ruleset to publish its docs

  * Improve ruleset documentation, like by adding more copy-paste examples

  * Suggest usability improvements to the Next.js rendering
</BlogPost>
