feat(custom-widgets): Full Mantine v9 + SubFetch support for custom JSX (#6304)

This commit is contained in:
Thomas authored and GitHub committed 2026-07-15 14:06:02 +02:00
1 parent 0a0b3a12c9
commit 3b2fc2527e
181 files changed
+18472 -4364

No files matched your search

+24 -14
View File
@@ -5,43 +5,47 @@ tags:
- Help
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
### Can I install Homarr on a Raspberry Pi?
Yes, Homarr is compatible with the architecture of the Raspberry Pi. Have a look in our <a href="/docs/category/installation-1">installation guide</a> on how to do so.
### I have a suggestion / idea for improvement or want to report a bug
You can report issues, suggestions, or ideas for improvements <a href="https://github.com/homarr-labs/homarr/issues/new/choose">here</a>.
### Can I use my own custom icons for apps?
Yes, check out our guide on <a href="/docs/advanced/customizations/icons">how to add your custom icons</a>.
### Some of my online icons are not working (anymore)
It seems the `cdn.jsdelivr.net` database of icon sometimes appear in the lists for DNS-Holes (PiHole, AdGuard Home...). <br />
So make sure to manually whitelist this address.
### Can I add my own widgets? Do I need programming skills?
We have decided, not to support custom widgets, until Homarr is in a stable and highly polished state.
Enabling users to write their own widgets is easy at the first glance, but there are some major issues with it:
- There are mainly two options for adding plugins or custom widgets to Homarr. The first option is, to allow and inject custom code in Homarr. Apps like Nextcloud can do this easily, because they use the Runtime to render all components of the application (In reality, the process is still a bit more complicated). In contrast, Homarr is using Next.js - a framework that needs to be compiled in production. That means, that each time you would modify a custom widget or plugin, the app would have to be restarted. There are options to avoid this, but they are quite flakey and bring disadvantages in our experience. The second option is, to write widgets declaratively instead of imperatively. This way, we would be able to render and modify widgets without any restarting at all. However, we'd have to write our own interpreter for said declarative schema. This is totally possible, but requires a lot of planning and architecture design to function properly. The required work to implement either option is therefore quite substantial.
- Custom widgets would need to be able to request data, so they work properly. We use tRPC for this. Many APIs use different formats and we'd have to convert and read those.
- Using custom widgets, one could easily create harmful widgets, leak your personal data (potentially including your tokens) and trick your users. A proper implementation (eg. official plugin store, where we can ban harmful widgets from) would resolve this problem to a certain extent, but again requires additional work and time to be put in.
Yes. Administrators can create [Custom Widgets](/docs/management/custom-widgets) that read an HTTP API and render the response with a built-in display type. These display types require no programming.
If you still want to add a widget, please consider to contribute it directly to Homarr. For that, basic programming skills are required. Our [Developer Guides](/docs/advanced/development/getting-started) explain this even more detailed.
Custom JSX is available when you need a more specific layout or an interactive control. It uses a restricted declarative JSX interpreter with a classified Mantine component set. Network operations are declared as named, same-origin requests and are checked against board permissions on the server. It does not execute arbitrary JavaScript or expose browser `fetch`.
You should be comfortable reading JSON and JSX to author complex templates. The management workbench includes examples, diagnostics, a component reference, API response inspection, and a prompt you can copy into an AI assistant. Always review imported templates and request capabilities before saving them.
### Is it secure to expose Homarr to the web?
Yes, since v0.14.0, we are confident that Homarr is secure enough to be exposed to the web.
Yes, since v0.14.0, we are confident that Homarr is secure enough to be exposed to the web.
Make sure to enable the "allow anonymous access" [option in the settings](/docs/management/boards#access-control), if you want to allow people without an account to be able to see your dashboard (not edit)
### How do I open the console / log?
There are two different sources of information:
- console: This is your client (browser). It can tell us, if there is an issue on the client (crash, bug, ...).
- log: This is your server. It will tell us, if there are issues with connections for the integrations, crashes and errors.
Both variants *can* contain personal data. It's recommended, that you don't post them publicly or remove personal data from it.
Both variants _can_ contain personal data. It's recommended, that you don't post them publicly or remove personal data from it.
<Tabs>
<TabItem value="apple" label="Read console" default>
@@ -49,39 +53,45 @@ Both variants *can* contain personal data. It's recommended, that you don't post
> Note: This will not work on most mobile browsers.
Open your browser and ensure, that Homarr is your current tab.
Press ``CTRL`` + ``SHIFT`` + ``I`` on your keyboard. If this shortcut doesn't work, please use one of the alternatives: https://developer.chrome.com/docs/devtools/open/.
Click on ``Console`` in the new window, that opened. There, you should see white, orange and sometimes red messages. You can use the Windows Snip & Sketch tool (or your respective tool on Linux / MacOS) to screenshot those messages.
Press `CTRL` + `SHIFT` + `I` on your keyboard. If this shortcut doesn't work, please use one of the alternatives: https://developer.chrome.com/docs/devtools/open/.
Click on `Console` in the new window, that opened. There, you should see white, orange and sometimes red messages. You can use the Windows Snip & Sketch tool (or your respective tool on Linux / MacOS) to screenshot those messages.
> Please never execute any commands in the console. Scammers are using the console to let you execute harmful commands. If you want to resolve an issue in Homarr, **you'll never have to execute any commands here**
</TabItem>
<TabItem value="orange" label="Read log">
This depends on your installation method. If you use Unraid, you can simply click on the App and on ``Log``. If you have a bare Docker install, use ``docker container logs homarr``. If you used a different name than ``homarr`` for your container, you need to replace said argument. Here is some additional information about the command: https://geekflare.com/check-docker-logs/.
This depends on your installation method. If you use Unraid, you can simply click on the App and on `Log`. If you have a bare Docker install, use `docker container logs homarr`. If you used a different name than `homarr` for your container, you need to replace said argument. Here is some additional information about the command: https://geekflare.com/check-docker-logs/.
</TabItem>
</Tabs>
### How do I post logs?
Please check [the section above](#how-do-i-open-the-console--log), on how to obtain logs. Logs should always be posted to https://pastebin.com/ or https://rentry.co/.
### Can I use Homarr at my company?
Yes, you can! But please consider a donation, if our work has been useful to you.
### I think I found a bug. What can I do?
If you think, that you found a bug, please create an issue <a href="https://github.com/homarr-labs/homarr/issues/new/choose">here</a>.
### Why can't I use my custom icons?
You need to restart the Homarr Docker container after adding icons.
### Why can't homarr see/ping containers running locally behind GluetunVPN?
This is because GluetunVPN, and many other docker VPN services, secure their VPN connections with strict firewalls. These firewalls often block access to local containers, even on the same docker network. The workaround for <a href="https://github.com/qdm12/gluetun-wiki/blob/main/setup/options/firewall.md">GluetunVPN</a> is to add "- FIREWALL_OUTBOUND_SUBNETS=192.168.1.0/24" into the environmental variables of the docker compose file. If you are using a firewall on your system, you will need to add a rule that allows requests from ip's in the range of 192.168.xxx.xxx . If you are using UFW the command would be "sudo ufw allow from 192.168.0.0/16".
Tip: If you are running your torrent client behind a VPN, and running Sonarr (or something similar) normally, make sure to open your torrent client's port in your firewall. If you don't, Sonarr will not be able to see it. Furthermore, use your machines IP (192.168.x.x) rather then 127.0.0.1 or localhost.
### Can I get in touch with you?
Yes, go <a href="/docs/community/get-in-touch">here</a> to get with us in touch.
<hr/>
<hr />
:::tip
@@ -0,0 +1,26 @@
---
sidebar_label: AI and MCP authoring
---
# AI and MCP authoring
Homarr exposes an iterative, validation-first workflow to authenticated administrators. Agents should continue until validation, preview, and diagnostics are clean; they must not invent inline network access.
## Recommended loop
1. Read `customWidget_schema` and the generated component/capability reference.
2. Read an existing definition with `customWidget_byId`, or create a v3 draft.
3. Call `customWidget_validate`; fix every issue using its exact path.
4. Call `customWidget_preview` for the base GET and retain the preview session ID.
5. Test each named query with `customWidget_previewQuery`.
6. Dry-run every action with `customWidget_simulatePreviewAction`.
7. Inspect `customWidget_previewJournal` for status, duration, and method coverage.
8. Revalidate, then call import/create or a focused update/patch operation.
Stored secret values are never readable. Omitting secrets during an update preserves them. MCP actions are simulation-only; enabling live preview actions remains a browser-only administrator decision.
## Template format
Use multiline JSX when reading or patching templates. Prefer focused template read/write/patch tools over rewriting an escaped one-line JSON string. When a complete portable artifact is needed, use the authoring bundle placeholder plus a separate fenced JSX block.
The AI prompt is generated from the same registry, examples, request rules, and limits as the validator. Agents should still trust validation output over prompt memory because the installed Homarr version is authoritative.
@@ -0,0 +1,29 @@
---
sidebar_label: Maintainer architecture
---
# Maintainer architecture
Reusable Custom Widget behavior belongs to `@homarr/custom-widgets`:
- `core` owns schemas, descriptors, migrations, import/export, examples, diagnostics metadata, and prompt generation.
- `jsx` owns AST parsing, shared policy, interpreter, bindings, registry, wrappers, and analysis.
- `runtime` owns renderers and named-request controls through injected ports.
- `server` owns manifests, network execution, caching, limits, preview sessions, and typed domain errors.
- `workbench` owns form contracts, editor analysis, preview UI, response tree, and import review through injected ports.
The API package retains persistence and board authorization adapters. Widgets retain board registration and runtime transport wiring. Next.js routes retain navigation and shells. The domain package must never import API, widgets, or route modules.
## Add a display type
Extend the typed descriptor and discriminated schema, defaults/hydration/serialization, renderer, workbench section, preview, import round-trip fixtures, docs, and translations. Create, edit, import, export, MCP, and preview must all use the same transform.
## Add a JSX component
Classify the upstream export, document safety mode and supported props, add a wrapper if behavior needs constraining, and update registry tests. CI fails when a Mantine upgrade introduces an unclassified export.
## Change networking
Implement policy below the framework-neutral server boundary and expose infrastructure through a typed port. Add tests for permissions, typed substitution, DNS classes, redirects, headers, timeouts, limits, redaction, cache behavior, and action non-retry semantics before wiring the tRPC adapter.
Production files target 300 lines. Algorithmic AST/network modules may use a documented 400-line ceiling. Dependency-boundary, cycle, file-size, translation, generated-artifact, component-classification, focused unit, integration, E2E, accessibility, and docs-build gates protect the architecture. After a Next.js production build, `pnpm check:custom-widget-bundle` also enforces a 768 KiB gzip ceiling across the client chunks referenced by Custom JSX.
@@ -0,0 +1,36 @@
---
sidebar_label: Authoring workbench
---
# Authoring workbench
The Custom Widget form is a continuous workflow: choose a display, configure the HTTP connection, configure the display, then test it in the adjacent preview. Each section stays visible so you can revisit earlier choices without losing context.
## Choose a display
The display grid is the first product decision. Built-in displays are best for common metrics, tables, statuses, and actions. Choose Custom JSX when the layout needs multiple Mantine components, conditional presentation, charts, dates, or named requests.
Changing display type preserves shared connection fields and initializes safe display defaults. A Custom JSX template is never blank; new and migrated forms receive the tested starter fixture.
## Test and preview
The preview has four tabs:
- **Widget** renders at compact, standard, or wide board-like sizes.
- **Response** uses a tree to inspect data, copy paths, and insert paths into JSX. The external-response action opens a temporary local blob and never publishes the response.
- **Request** shows the redacted base request, named capabilities, status, duration, and preview journal.
- **Diagnostics** shows template size, named requests, network scope, and blocking or advisory issues.
Only GET base requests run automatically. Preview actions are simulated until an administrator explicitly enables live actions for the five-minute preview session. DELETE still requires confirmation.
## Keyboard and clipboard
The JSX and JSON editors support native Ctrl/Cmd+Z and redo, plus visible toolbar controls. Cursor position, character count, and the 50,000-character limit remain visible.
On the list page, paste a complete v3 export with Ctrl+V or Command+V to import it as new. On an edit page, pasting opens a replacement review. Nothing is applied before the origin, methods, credential inheritance, and permissions are shown.
For AI output, prefer the authoring bundle format: a JSON block containing `"template": "__HOMARR_TEMPLATE__"`, followed by a JSX block. This keeps multiline JSX readable and avoids JSON escape errors.
## Save behavior
Errors block save; warnings do not. Server validation runs again on every create or update. Leaving with dirty changes triggers protection. Secrets omitted during edit remain unchanged, and secrets are never exported.
@@ -0,0 +1,49 @@
---
sidebar_label: Custom JSX language
---
# Custom JSX language
Custom JSX is a declarative expression language, not browser JavaScript. Homarr parses an explicit AST subset and emits React elements from a classified component registry. Templates cannot access browser globals, native prototypes, constructors, refs, portals, raw event handlers, or direct `fetch`.
## Data and rendering
The base GET response is available as `data`. Safe member access, conditionals, arrays, object literals, and interpreter-created collection callbacks can compose displays:
```jsx
<Stack gap="xs">
{data.services
.filter((service) => service.visible)
.map((service) => (
<Group key={service.id} justify="space-between">
<Text>{service.name}</Text>
<Badge color={service.online ? "green" : "red"}>{service.status}</Badge>
</Group>
))}
</Stack>
```
Properties are resolved own-only. Reflective names such as `constructor`, `prototype`, `__proto__`, `caller`, `callee`, `arguments`, `bind`, `call`, and `apply` are rejected even when computed or Unicode escaped.
## Components and props
Every Mantine export is classified as allowed, wrapped, or denied. The [generated component reference](./#component-reference) is authoritative. Props are allowlisted per component; unsupported props produce warnings and are ignored. Polymorphic roots, unsafe URLs, portals, positioning escapes, refs, and function props are stripped.
`SubData` is a convenience for simple response paths. Prefer the render-child pattern for rich nested results:
```jsx
<SubFetch requestId="detail" params={{ id: data.selectedId }}>
{(detail, meta) => (
<Stack>
<Title>{detail.name}</Title>
<Text>HTTP {meta.status}</Text>
</Stack>
)}
</SubFetch>
```
## Safe helpers and budgets
Bindings include restricted conversion, string, array, object, JSON, math, URI, and pure date helpers. Native `Date` objects and native prototypes are not exposed.
Templates are limited to 50,000 characters, 10,000 rendered nodes, and explicit AST-depth, operation, collection, and string budgets. Budget errors stop only the Custom JSX renderer and are contained by its error boundary.
@@ -0,0 +1,66 @@
---
sidebar_label: Extension recipes
---
# Custom Widget extension recipes
These recipes keep every authoring and runtime path on the same typed source of truth. Run `pnpm check:custom-widgets` after each structural change and add focused package tests before changing an adapter.
## Add a built-in display type
1. Add the discriminated configuration schema in `@homarr/custom-widgets/core`.
2. Add one descriptor with its icon, category, localized metadata keys, defaults, hydration, serialization, and validation.
3. Add the renderer to the runtime display map. It must consume normalized display data and injected actions only.
4. Add a bounded workbench section that edits the descriptor-owned form fields.
5. Add one fixture and assert create, edit, v2/v3 import, export, MCP, and preview round trips against it.
6. Add translation keys and update the display table in the user guide.
Do not add route-specific conversion code. `buildDisplayFormValues` and `buildDisplayConfigFromFormValues` are the only form conversion boundary.
## Add or update a JSX component
1. Classify the Mantine export as `allowed`, `wrapped`, or `denied` in the component registry. Denied entries require a reason.
2. Declare supported props and subcomponents. Function props, refs, portals, polymorphic roots, and unsafe positioning stay denied unless a wrapper removes the unsafe behavior.
3. Implement wrappers in the JSX package and register them through the package runtime-component factory.
4. Add analyzer and renderer tests for accepted props, stripped props, reflective-property attacks, and the rendered-node budget.
5. Regenerate the docs reference and verify the editor completion and AI prompt include the same entry.
The component-classification test must pass against the locked Mantine version. Never expose an upstream namespace or native prototype as a shortcut.
## Add a named-request capability
1. Extend the request schema and manifest renderer in `core` and `server` without accepting a raw runtime URL, method, header, or body.
2. Enforce permission, network scope, redirect, DNS, header, body, response, concurrency, and rate policies in the framework-neutral server service.
3. Expose infrastructure through a typed server port. Translate domain errors only in the API adapter.
4. Add the control to runtime through the injected request port. Queries use query semantics; actions require a gesture and use mutation semantics.
5. Add preview simulation and redacted-journal coverage. Live actions remain an explicit administrator-only browser choice.
6. Update MCP validation and simulation tools without exposing stored secrets or unrestricted action execution.
Tests must prove actions are never mounted automatically, polled, retried, cached, or redirected. DELETE must retain full permission and confirmation.
## Add a workbench feature
1. Put reusable state, analysis, or presentation in `@homarr/custom-widgets/workbench`.
2. Receive localized messages and typed callbacks as props. The package cannot import the tRPC client, Next.js navigation, notifications, or Homarr widget adapters.
3. Keep the Next.js component as a transport and page-shell adapter.
4. Cover keyboard operation, focus order, reduced motion, 320/768/desktop layouts, dirty-state behavior, and error-boundary recovery.
5. Keep each production module below 300 lines and preserve the continuous two-pane form behavior.
## Change examples or AI authoring
Examples live as typed shared fixtures. Tests, snippets, documentation data, and the AI prompt consume those fixtures. AI output uses an authoring bundle: a JSON block with `__HOMARR_TEMPLATE__`, followed by a readable multiline JSX block. Do not require agents to escape an entire template into one JSON string.
After changing a fixture, validate every example, render it, regenerate the component reference, and run the prompt snapshot tests. MCP agents must be able to read a widget, patch template lines with a revision, validate, preview named queries, simulate actions, inspect the journal, and update the definition without receiving secret values.
## Required checks
```bash
pnpm check:custom-widgets
pnpm test
pnpm test:e2e
pnpm turbo typecheck --affected
pnpm turbo lint --affected
pnpm turbo build --filter=@homarr/docs
```
Also run the component-classification, generated-reference drift, bundle-budget, accessibility, and focused package/API suites configured by the repository. A change is incomplete if any create, edit, import, export, MCP, preview, or board-runtime path uses a separate transform.
+148 -152
View File
@@ -6,203 +6,199 @@ tags:
- JSX
---
import { CustomJsxComponentReference } from "@site/src/components/custom-jsx-component-reference";
# Custom Widgets
:::warning
Custom widgets connect a dashboard item to an HTTP API without compiling a new Homarr widget. Administrators choose the endpoint and authentication, then use a built-in display type or a Custom JSX template to render the response.
Custom widgets are a new feature. Share feedback, ask questions, and discuss ideas in our [GitHub Discussions](https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets).
## Guides
- [Authoring workbench](./authoring): create, edit, preview, paste, undo, import, and export.
- [Custom JSX language](./custom-jsx): supported syntax, components, bindings, diagnostics, and limits.
- [Named requests and security](./requests-and-security): permissions, network scopes, request manifests, and server protections.
- [AI and MCP authoring](./agent-authoring): an iterative agent workflow that validates and previews before saving.
- [Compatibility and migration](./migration): v1/v2 JSX and v2/v3 import behavior.
- [Troubleshooting](./troubleshooting): common editor, preview, permission, and networking failures.
- [Maintainer architecture](./architecture): package boundaries, extension points, and required tests.
- [Extension recipes](./extension-recipes): step-by-step changes for displays, JSX components, requests, and workbench features.
:::caution
A custom widget can expose data or perform actions with the credentials configured for it. Import definitions only from sources you trust and review every network capability before using the widget on a shared board.
:::
Custom widgets allow you to fetch data from any HTTP API and display it on your Homarr dashboard. You define the API endpoint, authentication, and how the response should be rendered - no code compilation or server restart required.
## Create a widget
## Creating a Custom Widget
Open **Management → Custom Widgets**, select **Add**, and configure:
To create a custom widget, navigate to the **Custom Widgets** section in the management sidebar and click the **Add** button. Fill in the configuration:
- **Name, description, and icon**: identify the widget in management and the widget picker.
- **URL**: the full HTTP or HTTPS base endpoint.
- **Authentication**: none, bearer token, basic authentication, API-key header, or API-key query parameter.
- **Display type**: a built-in renderer or Custom JSX.
- **Name** - Display name shown in the widget picker and on the dashboard
- **Description** - Optional description shown in tooltips and management UI
- **Icon URL** - Optional URL to an icon image for the widget
- **URL** - The API endpoint to fetch data from (must include protocol, e.g. `https://api.example.com/stats`)
- **Authentication** - How the API request is authenticated:
- `none` - Public APIs with no authentication
- `bearer` - Bearer token authentication
- `basic` - HTTP Basic auth (username + password)
- `apiKeyHeader` - API key sent as a custom HTTP header
- `apiKeyQuery` - API key sent as a query parameter
- **Header Name** - Custom header or query parameter name for API key auth (e.g. `X-API-Key`)
- **HTTP Method** - `GET`, `POST`, `PUT`, `PATCH`, or `DELETE`
- **Request Body** - JSON string body for `POST`/`PUT`/`PATCH` requests
- **Display Type** - How the API response is rendered (see below)
Use **Test** to run the current GET request. The preview workbench separates the rendered widget, sanitized response tree, redacted request journal, and diagnostics. It marks sample data as stale whenever the connection or display configuration changes after a test.
## Display Types
Testing creates a five-minute, administrator-bound preview session for unsaved named queries. Action controls are simulated by default. An administrator can explicitly enable live actions for that session; DELETE still requires confirmation.
| Display Type | Description | Best For |
| -------------------- | ---------------------------------------------------- | ---------------------------------------- |
| **Single Value** | Extracts a single value with a label and unit | Metrics: temperature, uptime, user count |
| **Key Value** | Multiple labeled key-value pairs with optional units | System stats: CPU, memory, disk |
| **Table** | API array data rendered as a sortable table | Lists of items with columns |
| **Stat Grid** | Labeled values in colored stat cards | Counts: movies, series, albums |
| **Progress Bars** | Values as progress bars with percentages | Usage: disk, quota, capacity |
| **Status Indicator** | Green/red status dots for health checks | Service monitoring |
| **Count Grid** | Simple counts in a grid (no colors) | Library counts |
| **Raw** | Pretty-prints the raw JSON response | Debugging |
| **Action Button** | Clickable button executing the API request | Triggers: restart, clear cache |
| **Custom JSX** | Custom JSX template with Mantine v9 components | Full layout control |
## Display types
### AI-Powered JSX Generation
| Display type | Use it for |
| ---------------- | --------------------------------------------------------- |
| Single Value | One metric with a label and unit |
| Key Value | Several labeled values |
| Table | Arrays of records |
| Stat Grid | Compact colored metrics |
| Progress Bars | Capacity, quota, and completion |
| Status Indicator | Health and availability |
| Count Grid | Compact counters |
| Raw | Sanitized JSON inspection |
| Action Button | One explicit API action |
| Custom JSX | A declarative Mantine layout with optional named requests |
When editing or creating a custom widget with the **Custom JSX** display type, you'll find a **Copy Prompt** button. Clicking it copies a pre-formatted prompt describing your widget's name, API endpoint, and available Mantine components. Paste this into your preferred AI assistant (ChatGPT, Claude, Copilot, etc.) to generate a JSX template tailored to your data.
## Custom JSX workbench
<details>
<summary>Example: Pokédex widget using Custom JSX</summary>
The JSX editor provides syntax highlighting, formatting, undo/redo buttons and Ctrl/Cmd+Z shortcuts, a searchable component reference, character and cursor status, and diagnostics. Errors block saving; warnings identify behavior that still receives server-side validation. Templates can contain up to 50,000 characters and render up to 10,000 nodes.
The template reads the base GET response from `data`:
```jsx
<Stack gap="xs" p="sm">
<Text fw={600}>{data.name}</Text>
<Badge color={data.online ? "green" : "red"}>{data.status}</Badge>
</Stack>
```
The **Copy AI prompt** action is available on desktop and mobile. Its prompt, component list, and examples are generated from the same metadata used by the renderer, so the reference stays aligned with the installed Homarr version.
### JSX API versions
- **Version 2** is the default. Network access is declared in a named-request manifest and enforced by the server.
- **Version 1** remains available for display-only templates. Inline network components are disabled until the definition is reviewed and migrated.
Definitions without `jsxApiVersion` are treated as version 1. Display-only templates can be migrated by selecting version 2 and saving an empty request list.
## Named requests
Version 2 templates never choose an arbitrary URL, method, header, or body at runtime. An administrator declares each allowed operation once, and the template references its `requestId` with typed parameters.
```json
{
"$schema": "homarr-custom-widget-v2",
"name": "Pokédex",
"description": "Browseable Pokémon list",
"iconUrl": "https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/items/poke-ball.png",
"url": "https://pokeapi.co/api/v2/pokemon?limit=75",
"authType": "none",
"method": "GET",
"displayType": "customJsx",
"displayConfig": {
"type": "customJsx",
"template": "<Stack gap=\"md\" p=\"xs\">\n <Card withBorder radius=\"xl\" p=\"md\" shadow=\"md\">\n <Group justify=\"space-between\">\n <Title order={3}>Pokédex</Title>\n </Group>\n </Card>\n <PaginatedList pageSize={12}>\n <Grid gutter=\"sm\">\n {data.results.map((pokemon, i) =>\n <Grid.Col span={1.5}>\n <Anchor href={pokemon.url} target=\"_blank\" underline=\"never\">\n <Card withBorder radius=\"xl\" p=\"xs\" shadow=\"md\">\n <Stack gap=\"sm\" align=\"center\">\n <Group justify=\"space-between\" style={{ width: \"100%\" }}>\n <Badge size=\"sm\" color=\"red\">#{i + 1}</Badge>\n <Text fw={800} tt=\"capitalize\">{pokemon.name}</Text>\n </Group>\n <Avatar\n src={\"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/\" + (i + 1) + \".png\"}\n size={92}\n radius=\"xl\"\n />\n </Stack>\n </Card>\n </Anchor>\n </Grid.Col>\n )}\n </Grid>\n </PaginatedList>\n</Stack>"
}
"type": "customJsx",
"jsxApiVersion": 2,
"networkScope": "public",
"template": "<SubFetch requestId=\"service-detail\" params={{ id: data.id }}><SubData path=\"status\" as=\"Badge\" /></SubFetch>",
"requests": [
{
"id": "service-detail",
"kind": "query",
"method": "GET",
"pathTemplate": "/api/services/{id}",
"parameters": { "id": "string" },
"auth": "inherit",
"minimumBoardPermission": "view",
"cacheTtlSeconds": 30
}
]
}
```
</details>
Path placeholders use `{parameter}` and are URL-encoded by the server. JSON body placeholders use `{ "$param": "parameter" }` and preserve the declared string, number, or boolean type.
## Importing and Exporting
`SubData` remains available for simple paths. For richer nested output, pass an interpreter-created render child; it receives the response data and safe status metadata:
Custom widgets can be exported and imported as JSON files. To export, select the widget in the list and click the export button. To import, click the import button and paste the JSON. Secrets (API keys, passwords) are never included in exports and must be configured after import.
```jsx
<SubFetch requestId="service-detail" params={{ id: data.id }}>
{(detail, meta) => (
<Stack gap="xs">
<Badge color={detail.online ? "green" : "red"}>{detail.status}</Badge>
<Text size="xs">HTTP {meta.status}</Text>
</Stack>
)}
</SubFetch>
```
The import format uses `"$schema": "homarr-custom-widget-v2"` to identify the widget version.
| Component | Request kind | Behavior |
| --------------- | ------------ | ------------------------------------------------------------- |
| `SubFetch` | `query` | Loads a named GET request automatically or manually |
| `ActionButton` | `action` | Runs a named POST, PUT, PATCH, or DELETE after a user gesture |
| `ToggleSwitch` | `action` | Passes declared on/off parameters and rolls back on failure |
| `RefreshButton` | None | Refreshes the parent GET response |
## Available Mantine Components for Custom JSX
Queries require board **view** permission. POST, PUT, and PATCH actions require **modify** by default. DELETE requires **full** permission and confirmation. Allowing viewers to execute a non-DELETE action requires explicit administrator approval. Anonymous viewers cannot execute actions.
When using the `customJsx` display type, your template has access to a whitelist of [Mantine v9](https://mantine.dev) components. Unrecognized components, event handlers, and dangerous tags are stripped for security.
### Network scopes
### Layout
| Scope | Allowed destinations |
| ---------- | ------------------------------------------------------------ |
| `public` | Public IP addresses only |
| `private` | Public addresses plus RFC1918 and IPv6 ULA private addresses |
| `loopback` | Public, private, and loopback addresses |
| Component | Doc |
| ------------------- | --------------------------------------------- |
| `Stack` | [docs](https://mantine.dev/core/stack) |
| `Group` | [docs](https://mantine.dev/core/group) |
| `Flex` | [docs](https://mantine.dev/core/flex) |
| `Grid` / `Grid.Col` | [docs](https://mantine.dev/core/grid) |
| `SimpleGrid` | [docs](https://mantine.dev/core/simple-grid) |
| `Center` | [docs](https://mantine.dev/core/center) |
| `Container` | [docs](https://mantine.dev/core/container) |
| `Space` | [docs](https://mantine.dev/core/space) |
| `AspectRatio` | [docs](https://mantine.dev/core/aspect-ratio) |
Link-local, cloud-metadata, unspecified, multicast, documentation, carrier-grade NAT, and IPv4-mapped bypass addresses remain blocked in every scope.
### Typography & Content
### Example with a named query
| Component | Doc |
| ----------------- | ------------------------------------------------- |
| `Text` | [docs](https://mantine.dev/core/text) |
| `Title` | [docs](https://mantine.dev/core/title) |
| `Code` | [docs](https://mantine.dev/core/code) |
| `Highlight` | [docs](https://mantine.dev/core/highlight) |
| `Mark` | [docs](https://mantine.dev/core/mark) |
| `Kbd` | [docs](https://mantine.dev/core/kbd) |
| `Blockquote` | [docs](https://mantine.dev/core/blockquote) |
| `Anchor` | [docs](https://mantine.dev/core/anchor) |
| `NumberFormatter` | [docs](https://mantine.dev/core/number-formatter) |
The editor and AI prompt include a shared Pokédex fixture that loads details only for the visible page. Its named request is equivalent to:
### Data Display
```json
{
"id": "pokemon-detail",
"kind": "query",
"method": "GET",
"pathTemplate": "/api/v2/pokemon/{name}",
"parameters": { "name": "string" },
"auth": "none",
"minimumBoardPermission": "view",
"cacheTtlSeconds": 300
}
```
| Component | Doc |
| ------------------------- | ------------------------------------------------- |
| `Badge` | [docs](https://mantine.dev/core/badge) |
| `Card` / `Card.Section` | [docs](https://mantine.dev/core/card) |
| `Paper` | [docs](https://mantine.dev/core/paper) |
| `Alert` | [docs](https://mantine.dev/core/alert) |
| `ThemeIcon` | [docs](https://mantine.dev/core/theme-icon) |
| `ColorSwatch` | [docs](https://mantine.dev/core/color-swatch) |
| `Avatar` / `Avatar.Group` | [docs](https://mantine.dev/core/avatar) |
| `Image` | [docs](https://mantine.dev/core/image) |
| `BackgroundImage` | [docs](https://mantine.dev/core/background-image) |
| `Indicator` | [docs](https://mantine.dev/core/indicator) |
| `Pill` | [docs](https://mantine.dev/core/pill) |
| `Spoiler` | [docs](https://mantine.dev/core/spoiler) |
## Agent authoring through MCP
### Tables
Administrators can use Homarr's MCP server to iterate on custom widgets without bypassing validation or network controls. A typical agent workflow is:
| Component | Doc |
| --------------------------------------- | -------------------------------------- |
| `Table` / `Table.Thead` / `Table.Tbody` | [docs](https://mantine.dev/core/table) |
| `Table.Tr` / `Table.Th` / `Table.Td` | [docs](https://mantine.dev/core/table) |
1. Call `customWidget_schema` to inspect the current import format.
2. Generate a complete `homarr-custom-widget-v3` draft.
3. Call `customWidget_validate` and correct every returned issue.
4. Call `customWidget_preview` to test the base GET request through Homarr's hardened executor.
5. Use `customWidget_previewQuery` for named queries and `customWidget_simulatePreviewAction` to verify action parameters without sending mutations.
6. Inspect `customWidget_previewJournal`, then validate again.
7. Call `customWidget_import`, or read an existing widget with `customWidget_byId` and apply a focused `customWidget_update`.
### Lists
MCP preview actions are simulation-only. Enabling live preview actions remains an explicit administrator choice in the browser workbench. Stored secret values are never returned by MCP; omit `secrets` when updating an existing widget to preserve them.
| Component | Doc |
| ---------------------------- | ----------------------------------------- |
| `List` / `List.Item` | [docs](https://mantine.dev/core/list) |
| `Timeline` / `Timeline.Item` | [docs](https://mantine.dev/core/timeline) |
## Component reference
### Feedback & Loading
Custom JSX exposes a classified subset of Mantine Core, Charts, Dates, and Homarr components. Allowed components render directly. Wrapped components receive safe props or read-only behavior. Denied components remain in the registry so dependency upgrades cannot silently expand the template surface.
| Component | Doc |
| ------------------------------- | ---------------------------------------------- |
| `Progress` / `Progress.Section` | [docs](https://mantine.dev/core/progress) |
| `RingProgress` | [docs](https://mantine.dev/core/ring-progress) |
| `Skeleton` | [docs](https://mantine.dev/core/skeleton) |
| `Loader` | [docs](https://mantine.dev/core/loader) |
<CustomJsxComponentReference />
### Navigation & Overlay
This table and the searchable component list in the editor are generated from Homarr's runtime registry.
| Component | Doc |
| ------------------------------------------------------------------------ | -------------------------------------------- |
| `TabsContainer` / `TabPanel` | Custom interactive component |
| `Accordion` / `Accordion.Item` / `Accordion.Control` / `Accordion.Panel` | [docs](https://mantine.dev/core/accordion) |
| `ScrollArea` | [docs](https://mantine.dev/core/scroll-area) |
| `Tooltip` | [docs](https://mantine.dev/core/tooltip) |
Component props are filtered. Polymorphic roots, portals, refs, unsafe positioning, raw event handlers, and `dangerouslySetInnerHTML` are not exposed. Direct browser globals and direct `fetch` calls are unavailable, while ordinary text containing the word “fetch” is valid.
### Charts
Safe bindings include conversion helpers, a restricted `Math` and `JSON` surface, object and array helpers, URI encoding helpers, and pure date formatting helpers. Collection callbacks and rendered output are subject to interpreter budgets.
| Component | Doc |
| ---------------- | --------------------------------------------------- |
| `AreaChart` | [docs](https://mantine.dev/charts/area-chart) |
| `BarChart` | [docs](https://mantine.dev/charts/bar-chart) |
| `LineChart` | [docs](https://mantine.dev/charts/line-chart) |
| `DonutChart` | [docs](https://mantine.dev/charts/donut-chart) |
| `PieChart` | [docs](https://mantine.dev/charts/pie-chart) |
| `RadarChart` | [docs](https://mantine.dev/charts/radar-chart) |
| `RadialBarChart` | [docs](https://mantine.dev/charts/radial-bar-chart) |
| `Sparkline` | [docs](https://mantine.dev/charts/sparkline) |
## HTTP protections and limits
### Interactive Components
All base, preview, query, and action requests use the same server executor:
| Component | Description |
| --------------- | ---------------------------------------- |
| `PaginatedList` | Client-side pagination wrapper |
| `Collapsible` | Expandable/collapsible section |
| `StatBar` | Horizontal stat bar |
| `TypeBadge` | Type-styled badge |
| `Divider` | [docs](https://mantine.dev/core/divider) |
- Only HTTP and HTTPS are accepted. URL credentials and fragments are rejected.
- Named paths must stay on the base endpoint origin.
- DNS results are validated against the selected network scope and pinned for the request.
- Queries can follow up to three revalidated same-origin redirects. Actions do not follow redirects.
- Authorization, cookie, host, forwarding, proxy, content-length, and hop-by-hop headers cannot be supplied by a template.
- Request bodies are limited to 10 KiB. Decompressed responses are limited to 1 MiB, 32 JSON levels, and 50,000 JSON nodes.
- GET requests can be cached and deduplicated. Actions are never cached, polled, or retried automatically.
## Template Bindings
## Import and export
Your template receives:
New exports use `"$schema": "homarr-custom-widget-v3"`. Version 2 imports remain accepted. Secrets are never exported and must be entered again after import.
- `data` - The sanitized API response object. Access fields via `data.fieldName` or `data.results[0].name`.
- `String(v)` - Type-safe string conversion
- `Number(v)` - Type-safe number conversion
- `Boolean(v)` - Type-safe boolean conversion
- `Math` - Safe subset: `round`, `floor`, `ceil`, `abs`, `min`, `max`, `pow`, `sqrt`, `PI`
- `JSON.stringify(v)` - JSON serialization
- `Array.isArray(v)` - Array type check
- `Object.keys(v)`, `Object.values(v)`, `Object.entries(v)` - Object utilities
Review the base origin, network scope, named request methods, inherited authentication, and required permissions before saving an imported widget. A version 1 template that contains inline network components stays disabled until it is converted to named requests.
## Security Constraints
AI-generated Custom JSX can also be pasted as a Homarr authoring bundle: one fenced JSON block with `"template": "__HOMARR_TEMPLATE__"`, followed by one fenced JSX block containing the readable multiline template. Homarr combines the blocks before validation, avoiding JSON newline and quote escaping. Raw JSON imports remain supported.
- Event handlers (`onClick`, `onChange`, etc.) are stripped
- Dangerous HTML attributes (`dangerouslySetInnerHTML`) are stripped
- `script`, `iframe`, `object`, `embed`, `form`, `style`, `link`, `meta`, `base` tags are forbidden
- Template strings containing `eval`, `Function`, `import`, `require`, `globalThis`, `window`, `document`, `fetch` are rejected
- Anchor `href` attributes are validated to only allow `https://`, relative (`/`), and hash (`#`) URLs
On the custom-widget list, paste a complete export with Ctrl+V or Command+V to open the import review and create it as a new definition. On an edit page, the same shortcut opens a replacement review and fills the existing form after confirmation. Pasted definitions never include secrets.
@@ -0,0 +1,19 @@
---
sidebar_label: Compatibility and migration
---
# Compatibility and migration
Homarr preserves existing display definitions while moving network-enabled Custom JSX to the named-request model.
## JSX API versions
Missing `jsxApiVersion` means v1. Display-only v1 templates can be opened and saved as v2 with an empty request manifest. Inline `url`, `method`, `headers`, and body props are inert in v2.
Static v1 network components can be converted by creating a named request and replacing inline capability props with `requestId` and typed `params`. Dynamic target construction requires an administrator to design an explicit path template and parameter list. Existing non-GET base definitions stop polling and show a network-review state.
## Import versions
Exports use `homarr-custom-widget-v3`; v2 imports remain accepted. Import migration normalizes display configuration, preserves v1 semantics, and never imports secret values. The review screen must be accepted before persistence.
Test migration with the same base response and compare rendered output, query results, action simulation, permissions, and journal entries before enabling the migrated definition on shared boards.
@@ -0,0 +1,32 @@
---
sidebar_label: Requests and security
---
# Named requests and security
Custom JSX v2 uses a saved request manifest. Runtime components pass only a `requestId` and declared primitive parameters; they cannot change the URL, method, headers, authentication, or body shape.
## Capability kinds
`SubFetch` references a GET `query`. `ActionButton` and `ToggleSwitch` reference a POST, PUT, PATCH, or DELETE `action`. Base data is GET-only. Actions require an explicit gesture and are never polled, cached, retried automatically, or run on mount.
Default board permissions are view for queries, modify for POST/PUT/PATCH, and full for DELETE. Viewer actions require explicit high-risk approval; anonymous users cannot execute actions. DELETE always requires confirmation.
## Parameter substitution
Path placeholders use `{name}` and are URL-encoded by the server. Runtime parameters must exactly match all declared names and types. JSON bodies substitute `{ "$param": "name" }` recursively and preserve string, number, or boolean types.
## Network boundary
Named paths stay on the base URL origin. HTTP and HTTPS are accepted; URL credentials and fragments are rejected. DNS answers are classified and pinned. Public, private, and loopback scopes progressively allow destinations, but link-local, metadata, unspecified, multicast, documentation, carrier-grade NAT, and IPv4-mapped addresses are always blocked.
Queries may follow three revalidated same-origin redirects. Actions do not follow redirects. Credentials never cross origins. Reserved authentication, cookie, host, forwarding, proxy, content-length, security, and hop-by-hop headers cannot be declared.
## Limits
- Request body: 10 KiB UTF-8.
- Decompressed response: 1 MiB, 32 JSON levels, 50,000 JSON nodes.
- Per user/item: 60 queries, 10 actions, and 3 deletes per minute.
- Concurrency: 4 per user/item and 8 per definition.
Successful GETs can use manifest cache TTLs and single-flight deduplication. Logs contain redacted origin/method/error metadata, never secrets, request bodies, or query values.
@@ -0,0 +1,33 @@
---
sidebar_label: Troubleshooting
---
# Troubleshooting
## The preview says the template is required
New Custom JSX forms always include the starter fixture. If an imported definition has an empty template, reopen its import review; malformed imports must be corrected rather than previewed as JSX.
## A prop is ignored
The diagnostics name the component and prop. Check the generated component reference. Unsupported props are intentionally removed; use a supported layout primitive or wrapper prop instead of polymorphic, portal, ref, positioning, or raw handler props.
## A request does not run
Confirm that the component kind matches the manifest: `SubFetch` needs a query; action controls need an action. Parameter names and primitive types must match exactly. Base requests and queries require GET. Actions only run from a gesture, and edit mode disables controls.
## Network scope errors
Resolve the hostname from the Homarr server environment. Select private or loopback scope only when required and understood. Link-local and metadata destinations cannot be enabled by any scope. Redirects must remain on the base origin.
## Permission errors
The placed board item is the authorization anchor. A definition ID alone cannot authorize credential use. Verify board view/modify/full access, explicit viewer-action approval, and that the caller is not anonymous.
## Stale or failed preview data
Connection, authentication, body, method, secret, or manifest changes mark samples stale. Run the GET test again. Background query failures keep prior content visible and offer retry; actions roll toggles back and show error feedback.
## Import paste is ignored
Paste a complete v2/v3 JSON export or authoring bundle while the list or edit page is focused. Ordinary text and partial JSON are ignored so normal typing is not intercepted.
+78 -26
View File
@@ -73,13 +73,7 @@ Replace `<your-api-key>` with your actual API key. Never commit API keys to vers
"mcpServers": {
"homarr": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://your-homarr-instance.com/api/mcp/mcp",
"--header",
"ApiKey:<your-api-key>"
]
"args": ["-y", "mcp-remote", "https://your-homarr-instance.com/api/mcp/mcp", "--header", "ApiKey:<your-api-key>"]
}
}
}
@@ -126,25 +120,26 @@ curl -s -X POST \
Homarr exposes 50+ tools organized by namespace. Each tool is either a **query** (GET, read-only) or **mutation** (POST, write action).
| Namespace | Example Tools | Type |
| ------------------ | -------------------------------------------------------- | ---------- |
| `app` | List all apps, get app by ID, create/update/delete app | GET + POST |
| `apiKeys` | List API keys | GET |
| `beszel` | Systems status, alerts, historical system metrics | GET |
| `board` | List boards, get board by ID, create/update/delete board | GET + POST |
| `docker` | List containers, inspect container, start/stop/restart | GET + POST |
| `icon` | Search icons | GET |
| `info` | Server health, version info | GET |
| `integration` | List integrations, test connection, delete integration | GET + POST |
| `invite` | List invitations, delete invite | GET + POST |
| `calendar` | Calendar events from media servers | GET |
| `dnsHole` | DNS hole summary, toggle blocking | GET + POST |
| `downloads` | Active downloads, pause/resume/delete | GET + POST |
| `healthMonitoring` | Resource usage stats | GET |
| `patchmon` | Host patch and security update statistics | GET |
| `mediaRequests` | Approve/decline media requests | GET + POST |
| `mediaServer` | Media server sessions and stats | GET |
| `smartHome` | Execute automations, device states | GET + POST |
| Namespace | Example Tools | Type |
| ------------------ | --------------------------------------------------------- | ---------- |
| `app` | List all apps, get app by ID, create/update/delete app | GET + POST |
| `apiKeys` | List API keys | GET |
| `beszel` | Systems status, alerts, historical system metrics | GET |
| `board` | List boards, get board by ID, create/update/delete board | GET + POST |
| `docker` | List containers, inspect container, start/stop/restart | GET + POST |
| `icon` | Search icons | GET |
| `info` | Server health, version info | GET |
| `integration` | List integrations, test connection, delete integration | GET + POST |
| `invite` | List invitations, delete invite | GET + POST |
| `calendar` | Calendar events from media servers | GET |
| `customWidget` | Validate, preview, create, and iteratively update widgets | GET + POST |
| `dnsHole` | DNS hole summary, toggle blocking | GET + POST |
| `downloads` | Active downloads, pause/resume/delete | GET + POST |
| `healthMonitoring` | Resource usage stats | GET |
| `patchmon` | Host patch and security update statistics | GET |
| `mediaRequests` | Approve/decline media requests | GET + POST |
| `mediaServer` | Media server sessions and stats | GET |
| `smartHome` | Execute automations, device states | GET + POST |
The tools table on the MCP tab is dynamically generated - it always reflects the latest available procedures from your Homarr instance.
@@ -154,6 +149,8 @@ The tools table on the MCP tab is dynamically generated - it always reflects the
- Tools inherit the permissions of the API key's owner
- OAuth endpoints are rate-limited to prevent abuse
- Tool execution errors are contained - a failing tool call does not crash the MCP server
- Custom-widget MCP actions are simulation-only; live preview actions must be enabled explicitly in the browser workbench
- Custom-widget secret values are never returned through MCP
- All communication uses HTTPS (configure HTTP → HTTPS redirects on your reverse proxy)
## Use Cases
@@ -165,3 +162,58 @@ The tools table on the MCP tab is dynamically generated - it always reflects the
- **"How many users are on my Homarr instance?"** - AI lists all users (admin only)
- **"Toggle Pi-hole blocking for 5 minutes"** - AI interacts with DNS controls
- **"How are my servers doing?"** - AI fetches Beszel system stats including CPU, memory, disk, and alerts
- **"Build and refine a custom widget for this API"** - AI validates the draft, tests GET data and named queries, simulates actions, and creates or updates the widget
## Custom Widget Workflow for AI Agents
AI agents can create and iterate on custom widgets using these tools in sequence.
:::tip Editing an existing widget?
Skip to step 5. Use `customWidget_byId` to fetch it and `customWidget_readTemplate` / `customWidget_writeTemplate` to edit its template directly.
:::
### 1. Get the schema
Call `customWidget_schema` to receive the JSON Schema for the `homarr-custom-widget-v3` format. This tells the agent what fields are required and their types.
### 2. Draft and validate
Build a widget JSON object and call `customWidget_validate` with it. The tool returns structured issues (path + message) so the agent can fix and re-validate until the draft is clean.
### 3. Create the widget
Call `customWidget_import` with the validated `homarr-custom-widget-v3` draft. This returns the new widget `id`. Use `customWidget_create` only when you intentionally need its lower-level creation input, including secret fields.
### 4. Test with real data
Call `customWidget_preview` with the widget's connection info. This makes a real GET request through Homarr's hardened executor and returns the sanitized response plus a preview session ID.
For Custom JSX v2 widgets with named requests, use `customWidget_previewQuery` to test each named GET query, and `customWidget_simulatePreviewAction` to dry-run actions without network calls.
### 5. Iterate on the template
Use `customWidget_readTemplate` to get the current JSX template as plain text, then `customWidget_writeTemplate` to push updates. The write tool accepts either:
- `template` — a single string (newlines as `\n`)
- `templateLines` — an array of strings (joined with newlines server-side, avoids `\n` escaping)
Both options validate the template AST before saving. This avoids re-sending the full widget JSON for template-only edits.
For small changes, prefer `customWidget_patchTemplate`. Pass the `revision` returned by `customWidget_readTemplate` and one or more line edits. Homarr rejects stale revisions, applies all edits atomically, and validates the complete template against its named request manifest before saving.
### 6. Update non-template fields
Call `customWidget_byId` to get the full widget, modify fields, and call `customWidget_update` with only the changed fields.
### Available components in templates
Custom JSX templates support 100+ Mantine components plus:
- `<TablerIcon name="server" size={20} color="blue" />` — 100+ curated Tabler icons by kebab-case name (e.g. `"player-play"`, `"alert-triangle"`)
- `<SubFetch>` / `<ActionButton>` / `<ToggleSwitch>` — network-capable components for named requests
- Array methods: `map`, `filter`, `find`, `findIndex`, `some`, `every`, `reduce`, `sort`, `slice`, `flat`, `reverse`, `at`, `pop`, `join`, `includes`, `indexOf` (note: `pop` returns last element without mutating)
- String methods: `toUpperCase`, `toLowerCase`, `trim`, `trimStart`, `trimEnd`, `slice`, `substring`, `includes`, `startsWith`, `endsWith`, `split`, `replace`, `replaceAll`, `repeat`, `padStart`, `padEnd`, `charAt`, `indexOf`, `lastIndexOf`
:::note
All `customWidget_*` tools require admin-level API key permissions. The preview executor enforces rate limits (60 queries/min, 10 actions/min).
:::
+27 -17
View File
@@ -1,29 +1,39 @@
---
title: 'Custom API'
description: 'Displays data from a custom API endpoint using configurable display types and optional JSX templates.'
title: "Custom API"
description: "Displays data from an administrator-defined HTTP API using built-in renderers or safe Custom JSX."
hide_title: true
---
import { WidgetHeader } from '@site/src/components/widgets/header';
import { AddingWidget } from '@site/src/components/widgets/adding';
import { customApiWidget } from '.';
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { customApiWidget } from ".";
<WidgetHeader widget={customApiWidget} categories={["Custom", "API"]} />
<WidgetHeader
widget={customApiWidget}
categories={['Custom', 'API']}
/>
The Custom API widget displays an API definition created by an administrator in [Management → Custom Widgets](/docs/management/custom-widgets). A definition owns the endpoint, credentials, display configuration, and any named network capabilities used by Custom JSX.
This widget fetches data from a custom API endpoint defined in [Custom Widgets management](/docs/management/custom-widgets) and renders it using configurable display types.
Custom API widgets are created and configured through the admin management page. Each widget definition specifies the API endpoint, authentication, display type, and refresh interval.
See the [Custom Widgets documentation](/docs/management/custom-widgets) for full details on creating and configuring custom widgets.
### Adding the widget
### Add the widget
<AddingWidget />
Choose a custom-widget definition and a refresh interval. The board item stores only the selected definition and display options; credentials remain server-side.
:::note
Custom API widgets must first be created in **Management → Custom Widgets** before they appear in the widget picker.
A definition must be enabled and available to the board before it appears in the widget picker.
:::
### Permissions and actions
The base response and named query requests require permission to view the board. Named POST, PUT, and PATCH actions require permission to modify the board by default. DELETE requires full board permission and a confirmation. Anonymous viewers cannot execute actions.
Interactive controls are disabled while the board is in edit mode. Actions run only after an explicit user gesture and are never triggered by mounting, refresh polling, or automatic retries. Authoring previews simulate actions unless an administrator explicitly enables live actions for the short-lived preview session.
### Refresh behavior
The refresh interval applies to the base GET response. A Custom JSX query can declare its own cache duration and can be refreshed manually. Named actions can invalidate the parent response or related named queries after they succeed.
If the definition needs a non-GET base request, migrate it to a GET base response plus a named action. Existing non-GET Custom JSX definitions require review before network access is enabled.
See [Custom Widgets management](/docs/management/custom-widgets) for the editor, display types, named-request manifest, network scopes, security limits, and migration guidance.