Widget and iframe embed
Add widget.js and a div with your widget key, and the code inside it becomes an editor readers can run in any of 45 languages, with no account. Sites that only accept iframes get the same editor from an embed URL. This page lists every attribute, the JavaScript API and its events.
Put a runnable code example on any page with one script tag. Readers edit the code, change its input and run it, with no account. The widget uses a CodeMirror 6 editor and runs code over a WebSocket. It renders in Shadow DOM, so your page's styles and the widget's stay separate, and its height is fixed when it loads, so running code never shifts your page.
Quick Start
<!-- 1. Add the widget script (before </body>) -->
<script src="https://onlinecompiler.io/widget.js"></script>
<!-- 2. Add a div with your key, the code, and optional input -->
<div class="runcode" data-key="YOUR_API_KEY" data-lang="python-3.14">
<pre>name = input()
print(f"Hello, {name}!")</pre>
<pre data-stdin>Ada</pre>
</div>Your code and input
Put the code inside the widget's div in a <pre>, and the program's standard input in a <pre data-stdin>. Both must be direct children of the div. The code in a <pre> is readable, and indexed, before the script loads, but < and & must be escaped. For code full of angle brackets, such as C++ includes or generics, use <script type="text/plain"> instead, which needs no escaping. Shared indentation is removed, and Reset brings back your code and input after a reader edits them.
<!-- No escaping needed inside <script type="text/plain"> -->
<div class="runcode" data-key="YOUR_API_KEY" data-lang="g++-15">
<script type="text/plain">
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{1, 2, 3};
for (int x : v) std::cout << x * x << "\n";
}
</script>
</div>A widget without code of its own opens with a sample program and a language menu, as a playground.
Configuration
Configure each widget instance using data-* attributes:
| Attribute | Required | Default | Description |
|---|---|---|---|
| data-key | Yes | - | Your API key |
| data-lang | No | python-3.14 | Language of the code (any compiler key) |
| data-theme | No | dark | dark, light, or auto to follow the reader's system setting |
| data-height | No | Fits the code | Editor height in pixels. Without it, the editor fits the starting code, from 6 to 20 lines. The whole widget is 227 pixels taller, for the header, console and credit line. fill fills the div's own height instead, so give the div a height. |
| data-lang-select | No | Only without code | true or false to always show or hide the language menu. Edits in each language are kept when a reader switches. |
| data-readonly | No | Off | Readers can run the code and change its input, but not edit the code. setCode still works. |
Hidden code around the reader's
Add a <script type="text/plain" data-template> (or a <pre data-template>) to run code the reader doesn't see, such as imports, a main function or test calls. The reader's code replaces ##CODE##, and its later lines get the marker's indentation, so it can sit inside a Python function. Without a marker, the reader's code runs after the template. The template applies to the language in data-lang only, and line numbers in errors count its lines too.
<!-- Readers see and edit only the function body -->
<div class="runcode" data-key="YOUR_API_KEY" data-lang="python-3.14">
<script type="text/plain" data-template>
def area(width, height):
##CODE##
print(area(3, 4))
</script>
<pre>return width * height</pre>
</div>JavaScript API and events
Each widget sends events to its div. They bubble, so you can also listen on document and read event.target. Use them to check a reader's answer, record progress, or unlock the next step:
<div class="runcode" id="exercise" data-key="YOUR_API_KEY" data-lang="python-3.14">
<pre># Print the sum of the numbers from 1 to 10
print()</pre>
</div>
<p id="verdict"></p>
<script>
// Check the reader's answer each time they run it.
document.getElementById("exercise").addEventListener("runcode:result", (event) => {
const { exit_code, output } = event.detail;
const correct = exit_code === 0 && output.trim() === "55";
document.getElementById("verdict").textContent = correct ? "Correct!" : "Not yet, try again.";
});
</script>
<script src="https://onlinecompiler.io/widget.js"></script>| Event | When, and what event.detail holds |
|---|---|
| runcode:ready | The widget has started. detail.widget is the widget. |
| runcode:run | A run started. detail has the reader's code (without the template), input and compiler. |
| runcode:result | The program ran. detail is the result: output, error, status, exit_code, signal, truncated, time, total and memory, as in the WebSocket codeoutput event. |
| runcode:error | The program didn't run, or its result was lost. detail.message says why: the key was refused, an account limit was hit, the connection dropped, or no result came back within 60 seconds. |
To control a widget from your page, get it with OnlineCompilerWidget.get(element):
// The widget on an element, once the script has started it (null before).
const widget = OnlineCompilerWidget.get(document.getElementById("exercise"));
widget.setCode('print(sum(range(1, 11)))');
widget.setInput("");
widget.run();| Method | What it does |
|---|---|
| getCode() | The code in the editor, without the template. |
| setCode(code) | Replaces the code in the editor. Readers can undo it, and Reset still brings back your original. |
| getInput(), setInput(text) | Read or replace the Input tab. |
| run() | Runs the code, like the Run button. Does nothing while a run is in progress or when the editor is empty. |
| reset() | Goes back to your original code and input. |
| disconnect() | Closes the connection. The next run reconnects. |
Multiple Widgets
Add as many widgets as you need. Use class="runcode" for multiple widgets or id="runcode" for a single one. Each widget works independently, and connects only when it scrolls near the viewport, so a long page of examples doesn't open a connection for each one up front. Widgets added to the page later, as single-page sites and docs generators with instant navigation do, start on their own, and a widget taken off the page closes its connection. For setups in Docusaurus, MkDocs, Sphinx, Mintlify and WordPress, see runnable code examples in docs. For a live demo and how the widget compares with other embeddable code editors, see embeddable code editor.
<!-- Follows the reader's light or dark setting; editor fits the code -->
<div class="runcode"
data-key="YOUR_API_KEY"
data-lang="python-3.14"
data-theme="auto">
<pre>print("Hello")</pre>
</div>
<!-- A Java playground: light theme, 300px editor, language menu shown -->
<div class="runcode"
data-key="YOUR_API_KEY"
data-lang="openjdk-25"
data-theme="light"
data-height="300"
data-lang-select="true">
</div>
<!-- Multiple widgets on the same page work independently -->
<script src="https://onlinecompiler.io/widget.js"></script>Sites that only take an iframe
Many course platforms, site builders and wikis strip script tags but allow an <iframe>. For those, https://api.onlinecompiler.io/embed/ is a page holding one widget that fills the frame. Fill in your widget key and code, and paste the iframe it gives you:
<iframe src="https://api.onlinecompiler.io/embed/?key=YOUR_WIDGET_KEY&lang=python-3.14&theme=light#code=name%20%3D%20input(%22Your%20name%3F%20%22)%0Aprint(f%22Hello%2C%20%7Bname%7D!%22)&input=Ada"
title="Runnable Python example" width="100%" height="420" style="border: 0" loading="lazy"></iframe>| Parameter | What it sets |
|---|---|
| key | Your widget key (required) |
| lang | The compiler id, such as python-3.14 or g++-15; python-3.14 if left out |
| theme | dark (the default), light or auto |
| readonly | 1 to stop readers editing the code |
| #code= | The starting code, URL-encoded, after the # |
| #input= | The starting standard input, URL-encoded, after the # |
- The code and input go after the
#, which browsers never send to a server, so they don't appear in our logs and long programs fit. Reset brings them back after a reader edits them. - A key restricted to your domain can only be framed by pages on that domain, and every frame around the embed must be on it too. Opened on its own, outside a frame, the embed shows a notice instead of running.
- The console is 156 pixels tall and the editor gets the rest of the frame's height.
Questions
- Do readers need an account to run the code?
- No. Runs use your widget key and count towards your account's allowance. Give the key its own monthly limit to cap a public page.
- Can I check a reader's answer?
- Yes. Listen for the runcode:result event on the widget's element and compare event.detail.output with the answer, as in the JavaScript API example.
- Can readers edit only part of the program?
- Yes. Put the whole program in a script tag with data-template and mark the editable part with ##CODE##; readers see and edit only that part.