Command line
To use F# Formatting tools via the command line, you can use the fsdocs dotnet tool.
dotnet tool install fsdocs-tool
dotnet fsdocs [command] [options]
The build command
This command processes a docs directory and generates API docs for projects in the solution according to the
rules of API doc generation. The input accepted is described in content.
fsdocs build
The command line options accepted are:
Command Line Option |
Description |
|---|---|
|
Input directory of content (default: |
|
Project files to build API docs for outputs, defaults to all packable projects |
|
Output Directory (default 'output'). Ignored by |
|
Disable generation of the 'Other' category in the navigation bar for uncategorized docs |
|
Disable generation of API docs |
|
Disable project cracking |
|
Evaluate F# fragments in scripts |
|
Save images referenced in docs ( |
|
Don't add line numbers, the default is to add line numbers. |
|
Additional substitution parameters for templates |
|
The tool will also generate documentation for non-public members |
|
Do not copy default content styles, javascript or use default templates |
|
Clean the output directory. Ignored by |
|
How much to log: |
|
Display this help screen |
|
Display version information |
|
Provide properties to dotnet msbuild, e.g. --properties Configuration=Release Version=3.4 |
|
Additional arguments passed down as |
|
Fail if docs are missing or can't be generated |
The following command line options are also accepted but it is instead recommended you use settings in your .fsproj project files:
Command Line Option |
Description |
|---|---|
|
Source folder at time of component build ( |
|
Source repository for github links ( |
|
Assume comments in F# code are markdown ( |
Project file settings
Many fsdocs behaviours can be controlled via MSBuild properties in your .fsproj (or Directory.Build.props) file.
Property |
Default |
Description |
|---|---|---|
|
|
Required — enables XML doc generation so fsdocs can produce API docs for this project. |
|
|
Include this project even though its |
|
|
Treat |
|
|
Emit warnings for public members that have no documentation comments. |
|
(auto) |
Root source folder used when constructing source-link URLs. Equivalent to |
|
(auto from repo) |
Repository URL prefix for source links. Equivalent to |
|
(none) |
URL for the collection-name link in the navigation header. |
|
(none) |
Path to the logo image shown in the header. |
|
|
Alt text for the header logo (accessibility). |
|
(none) |
URL the logo links to. |
|
(none) |
Path to the favicon. |
|
|
Theme to use for generated HTML. |
|
(none) |
URL to the project licence, shown in the footer. |
|
(none) |
URL to the release notes, shown in the footer. |
|
|
Suppress the "Inherited from X" sections on type pages. |
|
|
Controls how generic type constraints are displayed in member tooltips. |
|
|
Generate |
The command will report on any .fsproj files that it finds, telling you if it decides to skip a particular file and why.
For example, a project will be skipped if:
- The project name contains ".Tests" or "test" (because it looks like a test project)
-
The project does not contain
<GenerateDocumentationFile>true</GenerateDocumentationFile> -
The project
OutputTypeis notLibrary. To include an executable project, add this to the project file:<FsDocsAllowExecutableProject>true</FsDocsAllowExecutableProject>
The watch command
This command serves the documentation from memory while you edit it. Nothing is written to disk: a page is
built the first time the browser asks for it and kept until a file that influences it changes. Static files
such as images and css are served from their source location. A browser will be launched automatically
(unless --nolaunch is specified).
fsdocs watch
The input directory (e.g. docs) and the extra content shipped with the tool are watched, together with the
project files, the solution-wide MSBuild files (Directory.Build.props and friends) and the project output
DLLs used for the API docs. Every change goes through the same pipeline: the file is
stat-ed, its content hashed when the content matters, and only a real change invalidates the pages that
depend on it. A byte-identical rewrite (for example by a formatter) invalidates nothing. Editing a heading in
a.md rebuilds a.html on the next request and refreshes the navigation of the other pages without
rebuilding them; a project DLL or project file change re-cracks the projects and rebuilds the API reference in the
background. The API reference is also built in the background at startup, so the first page does not wait
for it. A background reconciler walks the watched folders every two seconds as a guard against missed file system events.
A page that fails to build returns a 500 with the error message; the other pages keep working and the
process stays up, even with --strict.
The search index (index.json) and llms.txt are the only URLs that need every page, and they are only
requested when the search dialog is first opened.
Two diagnostic pages are served under the reserved /.fsdocs/ prefix:
-
/.fsdocs/doctor: the projects that were cracked, whether their design-time build ran and which substitutions it changed (properties set by MSBuild targets, such as a version computed from a changelog, are only known after it; a button runs it again), the substitutions and where they come from, the templates that were tried and chosen, the navigation with the source of every title, the routes, the state of every requested page, the recent file events, the collected errors and the recent log lines. /.fsdocs/doctor.json: the same as JSON, handy for troubleshooting with an LLM.
Adding a project to the solution, or changing a project's output path, still needs a restart. The same
parameters as build are accepted
(--output, --clean and --saveimages are ignored with a note), plus these:
Command Line Option |
Description |
|---|---|
|
Do not launch a browser window. |
|
URL extension to launch http://localhost: |
|
Port to serve content for http://localhost serving. |
|
Address to bind the server to (default |
|
The absolute URL of the site ( |
Searchable docs
When using the command-line tool a Fuse search index is automatically generated in index.json.
A search box is included in the default template via an HTML Dialog element.
To add search to your own _template.html:
- include an HTML element with id
search-btn - include a
dialogelement - include
fsdocs-search.jsscript
<button id="search-btn">Open search dialog</button>
<dialog>
<input type="search" placeholder="Search docs" />
<div class="results">
<ul></ul>
<p class="empty">Type something to start searching.</p>
</div>
</dialog>
<script type="module" src="{`{root}}content/fsdocs-search.js"></script>
Keyboard navigation
The default template can be used from the keyboard:
Key |
Action |
|---|---|
|
Open the search dialog |
|
Close the search dialog |
|
Go to the next / previous heading of the page |
|
Move the focus to the main menu / the content / the page menu |
|
Go to the next / previous link of the menu; |
On pages that have few headings of their own, j / k also stop at the links of the listing: the namespaces on the
API reference index, and the types and modules on a namespace page. Any link in your own content can join in by
carrying a data-fsdocs-nav attribute.
The API reference pages carry an "On this page" menu of their own. A namespace page lists its types and modules, each linking to its own page, and a type or module page lists its sections. The index of all namespaces has no menu, since the page is already that list.
The hotkeys are ignored while typing in a text box. They are implemented in fsdocs-hotkeys.js, which expects the
main, #content, #fsdocs-main-menu and #fsdocs-page-menu elements of the default template. To add them to your
own _template.html, include the script:
<script type="module" src="{`{root}}content/fsdocs-hotkeys.js"></script>
Embedding Images
Downloading Remote Images (--saveimages)
The --saveimages flag controls whether images referenced in your docs are downloaded and saved locally alongside the generated output. This is primarily useful for non-HTML output formats such as LaTeX (PDF) and Jupyter Notebook (.ipynb), which cannot reference remote URLs at display time.
Value |
Behaviour |
|---|---|
|
Images are referenced by their original URL; nothing is downloaded. |
|
Images are downloaded and saved for LaTeX and notebook outputs only. |
|
Images are downloaded and saved for all output formats including HTML and Markdown. |
Example:
fsdocs build --saveimages some
Embedding Images Generated by Scripts
When using --eval to evaluate literate F# scripts, you can embed images produced by code (e.g. charts, plots) directly into the HTML output.
Option 1: Inline Base64 image using include-it-raw
Write a helper that reads an image file and returns an HTML <img> tag with the image data embedded as a Base64 string. Then use (*** include-it-raw ***) to inject the raw HTML into the output:
let inlinePng (fileName: string) =
let bytes = System.IO.File.ReadAllBytes(fileName)
let b64 = System.Convert.ToBase64String(bytes)
sprintf """<img src="data:image/png;base64,%s" />""" b64
// Generate the image in your script, then embed it:
// myChart.SavePng("chart.png")
// (*** hide ***)
// inlinePng "chart.png"
// (*** include-it-raw ***)
The (*** hide ***) command suppresses the source code of the expression, so only the rendered image appears in the output.
Option 2: Custom HTML printer via fsi.AddHtmlPrinter
If your charting library produces values of a known type, you can register a custom HTML printer so that values of that type are automatically rendered as images whenever they appear via (*** include-it ***). See Embedding Script Output for details.
val string: value: 'T -> string
--------------------
type string = System.String
type File =
static member AppendAllBytes: path: string * bytes: byte array -> unit + 1 overload
static member AppendAllBytesAsync: path: string * bytes: byte array * ?cancellationToken: CancellationToken -> Task + 1 overload
static member AppendAllLines: path: string * contents: string seq -> unit + 1 overload
static member AppendAllLinesAsync: path: string * contents: string seq * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 1 overload
static member AppendAllText: path: string * contents: ReadOnlySpan<char> -> unit + 3 overloads
static member AppendAllTextAsync: path: string * contents: ReadOnlyMemory<char> * encoding: Encoding * ?cancellationToken: CancellationToken -> Task + 3 overloads
static member AppendText: path: string -> StreamWriter
static member Copy: sourceFileName: string * destFileName: string -> unit + 1 overload
static member Create: path: string -> FileStream + 2 overloads
static member CreateSymbolicLink:
path: string *
pathToTarget: string ->
FileSystemInfo
...
type Convert =
static member ChangeType: value: obj * conversionType: Type -> obj + 3 overloads
static member FromBase64CharArray:
inArray: char array *
offset: int *
length: int ->
byte array
static member FromBase64String: s: string -> byte array
static member FromHexString: utf8Source: ReadOnlySpan<byte> -> byte array + 5 overloads
static member GetTypeCode: value: obj -> TypeCode
static member IsDBNull: value: obj -> bool
static member ToBase64CharArray: inArray: byte array * offsetIn: int * length: int * outArray: char array * offsetOut: int -> int + 1 overload
static member ToBase64String: inArray: byte array -> string + 4 overloads
static member ToBoolean: value: bool -> bool + 17 overloads
static member ToByte: value: bool -> byte + 18 overloads
...
System.Convert.ToBase64String(bytes: System.ReadOnlySpan<byte>, ?options: System.Base64FormattingOptions) : string
System.Convert.ToBase64String(inArray: byte array, options: System.Base64FormattingOptions) : string
System.Convert.ToBase64String(inArray: byte array, offset: int, length: int) : string
System.Convert.ToBase64String(inArray: byte array, offset: int, length: int, options: System.Base64FormattingOptions) : string
FSharp.Formatting