How to add runnable code examples to MkDocs and Material for MkDocs
Install the free mkdocs-onlinecompiler plugin, add your widget key to mkdocs.yml, and write runnable after a code block's language. Readers get a code editor with a Run button in the page: they change the code or its input and run it, with no account. The code runs on OnlineCompiler.io in a sandbox, in any of 46 languages, so a static docs site can run Python, C++ or SQL.
What do readers see?
A runnable block shows the code in an editor, with a Run button and the output beside it. This is a Material for MkDocs page with one runnable Python example, after a reader pressed Run:

The page's Markdown is an ordinary code block with one extra word:
```python runnable
for n in range(1, 4):
print(n, "squared is", n * n)
```1 squared is 1
2 squared is 4
3 squared is 9Readers can change the range or anything else and press Run again, and the Input tab holds what the program reads.
How do I install the plugin?
pip install mkdocs-onlinecompilerThen add it to mkdocs.yml with your widget key:
# mkdocs.yml
plugins:
- search
- onlinecompiler:
key: YOUR_WIDGET_KEYListing plugins replaces MkDocs' default list, so keep search in it if your
site has search. It needs MkDocs 1.5 or later and works with Material for MkDocs and MkDocs' own themes.
Add it to the requirements file your docs build installs from, such as the one GitHub Actions or Read
the Docs uses. The package is on PyPI and its code is public on GitHub, so you can read exactly what it does.
How do I get a widget key?
- Open the Widgets page of the OnlineCompiler.io dashboard and sign in with Google. The account is free.
- Create a widget key. Set its allowed domain to your docs site's domain exactly as visitors see it:
docs.example.comandexample.comare different domains. Formkdocs serveonlocalhost, make a second key. - Put the key in the plugin's
keyoption.
To keep the key out of the config file, read it from the environment:
plugins:
- search
- onlinecompiler:
key: !ENV [ONLINECOMPILER_WIDGET_KEY, ""]How do I make a code block runnable?
Write runnable after the language of any fenced code block. The block's language picks
the compiler: python runs Python 3.14, cpp runs C++ with G++ 15, js runs Node.js 24, sql runs SQLite, and compiler ids such as openjdk-25 work too. Code blocks without the word stay as they are.
The code comes through exactly as written, <, & and quotes included:
```cpp runnable
#include <iostream>
int main() {
std::cout << "a & b " << (1 < 2) << "\n";
}
```a & b 1Runnable blocks work inside admonitions and content tabs (with Material's pymdownx.superfences),
and in pages built from snippets. Here the Rust runner printed hi from Rust, the Go one hi from Go, and the last tab stays a plain code block:
!!! tip "In an admonition"
```rust runnable
fn main() {
println!("hi from Rust");
}
```
=== "Go"
```go runnable
package main
import "fmt"
func main() { fmt.Println("hi from Go") }
```
=== "Plain"
```python
print("plain in a tab")
```To run a block with another compiler than its language suggests, add lang=; the first
block below runs Python with pandas 2 and printed 2.3.3 when we ran it. Other options,
such as a title, still work, and so does the attribute style:
```python title="frame.py" runnable lang=python-3.14-pandas2
import pandas as pd
print(pd.__version__)
```
``` {.py .runnable}
print("attribute style")
```Which options does the plugin have?
| Option | Default | What it does |
|---|---|---|
key | none | Your widget key. Without one, runnable blocks show as plain code and the build warns. |
theme | site | site follows the light or dark mode of Material for MkDocs or MkDocs' own theme as readers switch it. light, dark or auto (the reader's system setting) fix it. |
languages | {} | Your own code block languages, mapped to compiler ids: {py3: python-3.14}. |
With Material's instant navigation, runners start on each page as readers move around, with nothing to set up. The widget documentation covers what the widget itself can do.
What other ways are there to run code in MkDocs?
| Approach | Languages | Can readers change the code and run it? |
|---|---|---|
markdown-exec (its exec option) | Python, shell and others installed where the site is built | No: the code runs once when the site is built, and the page shows its output |
| Python in the browser (mkdocs-pyscript, pyodide-mkdocs-theme, Pyodide blocks) | Python only | Yes, in the reader's browser, which first downloads a Python runtime |
| This plugin | 46 languages, Python with NumPy and pandas included | Yes: the code runs in a server sandbox on OnlineCompiler.io, and the browser shows the editor and the output |
Build-time execution suits output that should never change, such as a generated table. For examples readers should try and change, in any language, use runnable blocks.
Can I do it without a plugin?
Yes. Load the widget script with extra_javascript, turn on md_in_html and
wrap a code block in a runcode div. The runnable
code examples page has the exact steps. The plugin is simpler: one word per block, and the script
loads only on pages that need it.
Why doesn't my runnable block run?
| What you see | Why | Fix |
|---|---|---|
| Plain code, and "No widget key, so runnable code blocks show as plain code" in the build | The plugin has no key, or the environment variable it reads is empty. | Set the key option. |
| Plain code, and "a runnable code block shows as plain code: OnlineCompiler.io doesn't run ..." in the build, with the page's name | The block's language isn't one OnlineCompiler.io runs, or lang= names no compiler. | Use a language from the compiler list, or map your own name with the languages option. |
| The build stops with "Aborted with 1 warnings in strict mode" | mkdocs build --strict stops on the warnings above. | Fix what the warning names. |
| "OnlineCompiler.io refused this widget's key (Origin not allowed for this API key)" | The key's allowed domain isn't the domain the page is on, often a preview or localhost. | Set the allowed domain to the address in the browser's address bar, or make a second key for previews. |
| "OnlineCompiler.io refused this widget's key (Authentication failed)" | The key was mistyped, deleted or turned off. | Copy the key again from the Widgets page. |
Frequently asked questions
How do I run Python code in MkDocs or Material for MkDocs?
Install mkdocs-onlinecompiler, add your widget key to mkdocs.yml, and write runnable after the language of a Python code block (```python runnable). Readers can then edit the code and run it in the page and see its output. The same works for C++, Java, Go, Rust, SQL and the other languages.
Is the plugin free?
Yes, and it is open source (MIT). Running code uses your OnlineCompiler.io account, whose free plan has 100,000 runs a month with no credit card. Each press of Run is one run.
Do readers need an account?
No. Readers edit and run the code without signing in. Only the docs owner needs an OnlineCompiler.io account, for the widget key.
Does the code run on the server that hosts my docs?
No. MkDocs builds a static site, and the code runs on OnlineCompiler.io, in a fresh sandbox for each run with no network access. GitHub Pages, Read the Docs or any static host works.
Does it slow down my docs?
No. Only pages with a runnable block load the widget script, without holding up the page, and each runner connects only when it scrolls into view. Pages without runnable blocks load nothing extra.
Can search engines and the site's search read the code?
Yes. The code is in the page's HTML as text, so search engines and MkDocs search index it, and readers see it before the editor loads. If OnlineCompiler.io can't be reached, readers still see the code; only the Run button needs the service.
Which languages can I use?
Python, C, C++, Java, C#, F#, Go, Rust, PHP, Ruby, Haskell, TypeScript, JavaScript, SQL, DuckDB, Bash, Fortran, Visual Basic, Kotlin, R, Swift, COBOL, Perl, Julia, Pascal, Octave, Assembly, Lua, OCaml, Common Lisp, Erlang, Elixir, Prolog, D, FreeBASIC, Dart, Clojure, Groovy, Scala, Zig, Nim, Crystal, Ada, PowerShell, Racket, Objective-C.
Is the widget key secret?
No. A widget key is made to sit in public pages: it can run code in the widget, but it can't call the REST API. Its allowed domain is what stops other sites from using it.