Skip to main content
Frontend Developmentexistential-birds

exdoc-config

Configures ExDoc for Elixir projects including mix.exs setup, extras, groups, cheatsheets, and livebooks. Use when setting up or modifying ExDoc documentation generation.

Stars
60
Source
existential-birds/beagle
Updated
2026-05-31
Slug
existential-birds--beagle--exdoc-config
View on GitHubRaw SKILL.md

// install — copy + paste into any project

mkdir -p .claude/skills && curl -fsSL https://raw.githubusercontent.com/existential-birds/beagle/HEAD/plugins/beagle-elixir/skills/exdoc-config/SKILL.md -o .claude/skills/exdoc-config.md

Drops the SKILL.md into .claude/skills/exdoc-config.md. Works with Claude Code, Cursor, and any agent that loads SKILL.md files from .claude/skills/.

ExDoc Configuration

Quick Reference

Topic Reference
Markdown, cheatsheets (.cheatmd), livebooks (.livemd) references/extras-formats.md
Custom head/body tags, syntax highlighting, nesting, annotations references/advanced-config.md

Gates

Use this sequence before claiming ExDoc is wired correctly or that docs build:

  1. Dependencies resolved — Run mix deps.get from the project root. Pass: exit code 0, and mix.exs includes ex_doc as in Dependency Setup (or the project’s equivalent dev-only docs dep).
  2. Extra paths real — For every path string in extras/0 (and in groups_for_extras/0 if used), confirm that path exists in the repo or you have just created that file. Pass: no stale or typo paths remain when you run mix docs.
  3. Docs build — Run mix docs. Pass: exit code 0, and the HTML entry exists at <output>/index.html (default <project>/doc/index.html; use docs: [output: ...] if you changed output).

For cheatsheets, livebooks, or custom head/body assets, follow the same “path exists before listing” rule; see When to Load References.

Dependency Setup

Add ExDoc to mix.exs deps:

defp deps do
  [
    {:ex_doc, "~> 0.34", only: :dev, runtime: false}
  ]
end

Project Configuration

Configure your project/0 function in mix.exs:

def project do
  [
    app: :weather_station,
    version: "0.1.0",
    elixir: "~> 1.17",
    start_permanent: Mix.env() == :prod,
    deps: deps(),

    # ExDoc
    name: "WeatherStation",
    source_url: "https://github.com/acme/weather_station",
    homepage_url: "https://acme.github.io/weather_station",
    docs: docs()
  ]
end

The docs/0 Function

Define a private docs/0 function to keep project config clean:

defp docs do
  [
    main: "readme",
    logo: "priv/static/images/logo.png",
    output: "doc",
    formatters: ["html", "epub"],
    source_ref: "v#{@version}",
    extras: extras(),
    groups_for_modules: groups_for_modules(),
    groups_for_extras: groups_for_extras()
  ]
end

Key Options

Option Default Description
main "api-reference" Landing page module name or extra filename (without extension)
logo nil Path to logo image displayed in sidebar
output "doc" Output directory for generated docs
formatters ["html"] List of output formats ("html", "epub")
source_ref "main" Git ref used for "View Source" links
assets nil Map of source directory to target directory for static assets
deps [] Links to dependency documentation

Setting the Landing Page

The main option controls what users see first:

# Use the README as the landing page (most common)
docs: [main: "readme"]

# Use a specific module as the landing page
docs: [main: "WeatherStation"]

# Use a custom guide
docs: [main: "getting-started"]

The value matches the extra filename without its extension, or a module name.

Extras

Extras are additional pages beyond the API reference. Add them as a list of file paths:

defp extras do
  [
    "README.md",
    "CHANGELOG.md",
    "LICENSE.md",
    "guides/getting-started.md",
    "guides/configuration.md",
    "guides/deployment.md",
    "cheatsheets/query-syntax.cheatmd",
    "notebooks/data-pipeline.livemd"
  ]
end

Controlling Extra Titles

By default, ExDoc uses the first h1 heading as the title. Override with a keyword tuple:

defp extras do
  [
    {"README.md", [title: "Overview"]},
    {"CHANGELOG.md", [title: "Changelog"]},
    "guides/getting-started.md"
  ]
end

Ordering

Extras appear in the sidebar in the order listed. Put the most important pages first:

defp extras do
  [
    "README.md",
    "guides/getting-started.md",
    "guides/architecture.md",
    "guides/deployment.md",
    "CHANGELOG.md"
  ]
end

Grouping

Grouping Modules

Organize modules into logical sections in the sidebar:

defp groups_for_modules do
  [
    "Sensors": [
      WeatherStation.Sensor,
      WeatherStation.Sensor.Temperature,
      WeatherStation.Sensor.Humidity,
      WeatherStation.Sensor.Pressure
    ],
    "Data Processing": [
      WeatherStation.Pipeline,
      WeatherStation.Pipeline.Transform,
      WeatherStation.Pipeline.Aggregate
    ],
    "Storage": [
      WeatherStation.Repo,
      WeatherStation.Schema.Reading,
      WeatherStation.Schema.Station
    ]
  ]
end

Use regex to group by pattern:

defp groups_for_modules do
  [
    "Sensors": [~r/Sensor/],
    "Schemas": [~r/Schema/],
    "Pipeline": [~r/Pipeline/]
  ]
end

Modules not matching any group appear under a default "Modules" heading.

Grouping Functions

Group functions within a module using groups_for_docs:

defp docs do
  [
    groups_for_docs: [
      "Lifecycle": &(&1[:section] == :lifecycle),
      "Queries": &(&1[:section] == :queries),
      "Mutations": &(&1[:section] == :mutations)
    ]
  ]
end

Tag functions in your module with @doc metadata:

@doc section: :lifecycle
def start_link(opts), do: GenServer.start_link(__MODULE__, opts)

@doc section: :queries
def get_reading(station_id), do: Repo.get(Reading, station_id)

Grouping Extras

Organize guides, cheatsheets, and notebooks in the sidebar:

defp groups_for_extras do
  [
    "Guides": [
      "guides/getting-started.md",
      "guides/configuration.md",
      "guides/deployment.md"
    ],
    "Cheatsheets": [
      "cheatsheets/query-syntax.cheatmd",
      "cheatsheets/ecto-types.cheatmd"
    ],
    "Tutorials": [
      "notebooks/data-pipeline.livemd",
      "notebooks/sensor-setup.livemd"
    ]
  ]
end

Use glob patterns for convenience:

defp groups_for_extras do
  [
    "Guides": ~r/guides\/.*/,
    "Cheatsheets": ~r/cheatsheets\/.*/,
    "Tutorials": ~r/notebooks\/.*/
  ]
end

Dependency Doc Links

Link to documentation for your dependencies so ExDoc cross-references resolve:

defp docs do
  [
    deps: [
      ecto: "https://hexdocs.pm/ecto",
      phoenix: "https://hexdocs.pm/phoenix",
      plug: "https://hexdocs.pm/plug"
    ]
  ]
end

This enables references like t:Ecto.Schema.t/0 to link directly to the dependency docs.

Generating Docs

# Generate HTML docs
mix docs

# Open in browser
open doc/index.html

Complete mix.exs Example

defmodule WeatherStation.MixProject do
  use Mix.Project

  @version "1.3.0"
  @source_url "https://github.com/acme/weather_station"

  def project do
    [
      app: :weather_station,
      version: @version,
      elixir: "~> 1.17",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      name: "WeatherStation",
      source_url: @source_url,
      homepage_url: "https://acme.github.io/weather_station",
      docs: docs()
    ]
  end

  defp docs do
    [
      main: "readme",
      logo: "priv/static/images/logo.png",
      source_ref: "v#{@version}",
      formatters: ["html"],
      extras: extras(),
      groups_for_modules: groups_for_modules(),
      groups_for_extras: groups_for_extras(),
      deps: [
        ecto: "https://hexdocs.pm/ecto",
        phoenix: "https://hexdocs.pm/phoenix"
      ]
    ]
  end

  defp extras do
    [
      "README.md",
      "CHANGELOG.md",
      "guides/getting-started.md",
      "guides/configuration.md",
      "guides/deployment.md",
      "cheatsheets/query-syntax.cheatmd",
      "notebooks/data-pipeline.livemd"
    ]
  end

  defp groups_for_modules do
    [
      "Sensors": [~r/Sensor/],
      "Data Processing": [~r/Pipeline/],
      "Storage": [~r/Schema|Repo/]
    ]
  end

  defp groups_for_extras do
    [
      "Guides": ~r/guides\/.*/,
      "Cheatsheets": ~r/cheatsheets\/.*/,
      "Tutorials": ~r/notebooks\/.*/
    ]
  end

  defp deps do
    [
      {:phoenix, "~> 1.7"},
      {:ecto_sql, "~> 3.12"},
      {:ex_doc, "~> 0.34", only: :dev, runtime: false}
    ]
  end
end

When to Load References

  • Setting up cheatsheets or livebooks as extras -> extras-formats.md
  • Injecting custom CSS/JS, configuring syntax highlighting, or tuning module nesting -> advanced-config.md