Docs menu
Your first pane
Published
This page builds one small pane from scratch. It shows the current temperature and wind for a place, from Open-Meteo’s free forecast API, which needs no key. Along the way you write all four files of a pane, call the API with dash.fetch, refresh it with dash.refresh, and place it on a canvas.
Create the pane
In the sidebar, open My panes and choose New pane. Call it “Weather now” and pick the Blank template. The editor opens with four tabs: index.html, style.css, component.js and manifest.json. Replace the contents of each tab with the code below.
The editor saves a draft as you type, and the preview on the right runs the latest saved draft. The console under the editor shows what the pane logs and every request it makes.
index.html
The pane’s markup. It is not a full page: it is placed inside the pane’s frame, so leave out <html>, <head> and <body>. A <script> tag here does not run; code goes in component.js.
<div class="pane">
<p id="place" class="place"></p>
<p id="temp" class="temp">–</p>
<p id="wind" class="muted"></p>
<p id="updated" class="muted"></p>
</div>
style.css
The frame gives the pane the app’s colours and fonts as CSS variables: --dash-bg, --dash-surface, --dash-text, --dash-muted, --dash-border, --dash-accent, --dash-radius and --dash-font. Use them and the pane matches the canvas. The frame is as high as the pane, so height: 100% fills it.
.pane {
box-sizing: border-box;
height: 100%;
padding: 12px;
display: flex;
flex-direction: column;
justify-content: center;
gap: 2px;
overflow: hidden;
background: var(--dash-surface);
color: var(--dash-text);
font-family: var(--dash-font);
border: 1px solid var(--dash-border);
border-radius: var(--dash-radius);
}
p {
margin: 0;
}
.place {
font-weight: 600;
}
.temp {
font-size: 32px;
font-weight: 700;
line-height: 1.1;
}
.muted {
font-size: 13px;
color: var(--dash-muted);
}
manifest.json
The manifest says how big the pane is, which settings it has, and which hosts it may call.
{
"name": "Weather now",
"description": "Current temperature and wind from Open-Meteo.",
"size": { "default": [3, 3], "min": [2, 2], "max": [6, 4] },
"props": {
"place": { "type": "string", "label": "Place name", "default": "Lisbon" },
"latitude": { "type": "number", "label": "Latitude", "default": 38.72, "min": -90, "max": 90 },
"longitude": { "type": "number", "label": "Longitude", "default": -9.14, "min": -180, "max": 180 }
},
"network": ["api.open-meteo.com"]
}
sizeis in grid cells, as[width, height]. The pane starts at 3 × 3 and can be resized from 2 × 2 up to 6 × 4.propsare the pane’s settings. Each one becomes a field in the pane’s settings on the canvas, and the code reads the values fromdash.props. Until you change a value, the pane gets thedefault.networklists the hostsdash.fetchmay call. A call to any other host is refused withPROXY_HOST_NOT_ALLOWED.
Every field and rule is in the manifest reference.
component.js
The code runs as a JavaScript module once the HTML and CSS are in place, so the elements already exist and top-level await works.
const el = (id) => document.getElementById(id);
async function load() {
const { place, latitude, longitude } = dash.props;
el('place').textContent = place;
const url =
'https://api.open-meteo.com/v1/forecast' +
`?latitude=${latitude}&longitude=${longitude}` +
'¤t=temperature_2m,wind_speed_10m';
try {
const res = await dash.fetch(url, { cache: 300 });
if (!res.ok) {
el('temp').textContent = `Error ${res.status}`;
return;
}
const { current, current_units: units } = await res.json();
el('temp').textContent = `${Math.round(current.temperature_2m)}${units.temperature_2m}`;
el('wind').textContent = `Wind ${current.wind_speed_10m} ${units.wind_speed_10m}`;
el('updated').textContent = `Updated ${new Date().toLocaleTimeString()}`;
} catch (err) {
// Dotpane stopped the call. err.code says why.
el('temp').textContent = '–';
dash.log('weather failed', err.code ?? String(err));
}
}
dash.refresh(600, load);
dash.onProps(load);
What each part does:
dash.fetchsends the request through Dotpane’s servers, so the browser’s CORS rules do not apply.cache: 300keeps a 200 answer for five minutes, so a reload within that time does not call the API again. The dash.fetch reference has every option.- An error status is not an exception. If the API answers 4xx or 5xx, the promise still resolves, with
res.okset to false. It rejects only when Dotpane itself stops the call, with aDashErrorwhosecodesays why. The codes are listed in Error codes. dash.refresh(600, load)runsloadnow and then every 10 minutes. The shortest interval is 15 seconds; a smaller number is raised to 15. Runs never overlap. While the pane is off screen or the browser tab is hidden, runs pause, and the pane catches up with one run when it is shown again. More in dash.refresh.dash.onProps(load)runsloadagain when you change the pane’s settings on the canvas.
Catch the errors you expect. On a canvas, an error that nothing catches stops the pane and shows “This pane stopped” with the message, until you reload it.
Try it in the preview
The preview runs your draft in a real pane frame. Pick one of its sizes (3 × 4, 4 × 6, 6 × 8) or type your own width and height, and choose Reload preview to start the pane again. The Preview props form lets you try another place before the pane is on a canvas.
If nothing shows, open the console: a refused call appears there with its code.
Save a version
A canvas runs the pane’s latest saved version, not the draft, so choose Save version in the editor’s header. If a file has a problem, such as a manifest field that breaks a rule, the problems are listed and choosing one opens the file at that spot. The version label in the header changes to “Draft · last saved v1”.
Place it on a canvas
- Open a canvas, or make one with the + next to Canvases in the sidebar.
- Choose Add pane, then Weather now. The pane lands in the first free spot at its default size, 3 × 3.
- Switch the canvas from View to Edit. The dots behind the grid get darker.
- Drag the pane by its handle to move it. Drag its corner to resize it.
A canvas has 12 columns unless you change it in the canvas settings (6 to 24 columns, and a cell height of 40 to 120 pixels; 64 is the default). Resizing stays within the manifest’s min and max, here 2 × 2 to 6 × 4, and panes never overlap. Changes to the layout are saved as you make them.
In Edit mode, select the pane to open its settings on the right:
- Size and position has W, H, X and Y fields, for exact numbers.
- Props has a field for each prop. Change the place name and coordinates here, and
dash.onPropsreloads the weather.
Editing the layout needs a window at least 768 pixels wide. On a narrower screen the canvas shows its panes stacked.
Next
- Add an API that needs a key: Secrets.
- Keep data between runs with dash.storage.
- React to the pane’s size with dash.onResize.