diff --git a/.agents/skills/homarr-custom-widget/SKILL.md b/.agents/skills/homarr-custom-widget/SKILL.md
new file mode 100644
index 000000000..3121374ab
--- /dev/null
+++ b/.agents/skills/homarr-custom-widget/SKILL.md
@@ -0,0 +1,28 @@
+---
+name: homarr-custom-widget
+description: Author safe Homarr Custom JSX v2 widgets from API documentation.
+---
+
+# Homarr Custom Widget
+
+Author one widget at a time. When connected to Homarr, retrieve `homarr://custom-widgets/schema` and only the component resources needed for the design. The live resources are authoritative for the installed release.
+
+1. Read the API documentation.
+2. Create one credential-free widget with keyed `sources`, keyed `requests`, optional keyed `options`, and safe JSX `template`.
+3. Validate, preview, test queries and simulated actions, visually inspect, and iterate.
+4. Configure deployment-specific source URLs and request credentials through Homarr; never repeat plaintext.
+5. Save only after narrow/wide, light/dark, loading, empty, error, and success states work.
+
+Use `{option:name}` or `$option` for saved options. Use `{param:name}` or `$param` for values supplied by `SubFetch`, `ActionButton`, or `ToggleSwitch`. Load queries cannot use invocation parameters. Templates read `data`, `status`, `options`, and temporary `inputs`.
+
+`SubFetch` with `trigger="manual"` renders its own load button. Pass a card or image through `triggerContent` with `triggerAriaLabel` when that content should launch the request. Its callback is `(result, meta)`; never author `onClick` or a fetch callback.
+
+Use standard Mantine compound names and deliberate visual hierarchy, spacing, restrained semantic color, and responsive layouts. Avoid excessive nested cards.
+
+Do not use imports, hooks, refs, raw HTML, raw event callbacks, browser requests, arbitrary functions, eval, bigint, npm packages, authored statement blocks, IIFEs, or recursion. Do not pretend MCP tools exist in an offline chat session.
+
+To install an existing widget, use `customWidget_workshopSearch`, inspect it with `customWidget_workshopGet`, then use `customWidget_workshopInstall`. Configure each self-hosted source afterward with `customWidget_sourceConfigure`, or call `customWidget_configurationRequestUser` so the user can enter the server URL and credentials directly in Homarr.
+
+Preview-scoped configuration expires with the preview. To persist user-entered values for a new widget, create the widget first, request configuration for its definition ID, then create a final preview with that definition ID so Homarr loads the stored credentials.
+
+When this skill is installed from the repository, optional offline details are in `references/schema.md`, `references/runtime.md`, and `references/security.md`. MCP-only clients should use the live Homarr resources instead.
diff --git a/.agents/skills/homarr-custom-widget/references/runtime.md b/.agents/skills/homarr-custom-widget/references/runtime.md
new file mode 100644
index 000000000..7c2cab770
--- /dev/null
+++ b/.agents/skills/homarr-custom-widget/references/runtime.md
@@ -0,0 +1,16 @@
+# Runtime
+
+Templates read `data.requestId`, `status.requestId`, `options.name`, and temporary `inputs.name`. Status contains `loading`, `ok`, `status`, `statusText`, and `error`.
+
+`bind="search"` creates an in-memory input. It is never persisted. Supply invocation values only through `params`, for example:
+
+```jsx
+
+
+ {(items) => {(items ?? []).map(item => {item.name})}}
+
+```
+
+`SubFetch` with `trigger="manual"` renders its own load button. To make a card or image launch the request, pass that node through `triggerContent` and provide `triggerAriaLabel`; Homarr supplies the click and keyboard behavior. Its optional second callback argument contains `{ ok, status, statusText, loading: false }`; loading and request failures are rendered by `SubFetch` before the callback runs. Do not author `onClick` or a fetch callback.
+
+Use expression callbacks for supported collection methods and trusted slots. Do not use callback blocks, IIFEs, authored recursion, or raw events. Bounded regex literals work only with safe string matching/replacement operations.
diff --git a/.agents/skills/homarr-custom-widget/references/schema.md b/.agents/skills/homarr-custom-widget/references/schema.md
new file mode 100644
index 000000000..ad1a1b1a0
--- /dev/null
+++ b/.agents/skills/homarr-custom-widget/references/schema.md
@@ -0,0 +1,22 @@
+# Schema
+
+```ts
+interface HomarrCustomWidgetV2 {
+ $schema: "homarr-custom-widget-v2";
+ name: string;
+ description?: string;
+ iconUrl?: string;
+ sources: Record;
+ requests: Record;
+ options?: Record;
+ template: string;
+}
+```
+
+Sources require `default`. A request defaults to the default source, query, GET, load, inherited auth, and view permission. An action defaults to manual and modify permission. DELETE uses full permission and confirmation.
+
+Use stable real URLs for public APIs and clear suggested URLs for self-hosted services. Homarr collects the installer's server URL, network scope, and credentials as source setup; credentials remain outside the manifest.
+
+Paths use `{option:name}` and `{param:name}`. Query/body data uses `{ "$option": "name" }` and `{ "$param": "name" }`. Load queries cannot use params. Runtime parameter names and primitive values are inferred from references.
+
+Every option has `label`, `control`, and `default`. Optional fields are `description`, `choices`, `choicesFrom`, `min`, `max`, `step`, `advanced`, and `group`.
diff --git a/.agents/skills/homarr-custom-widget/references/security.md b/.agents/skills/homarr-custom-widget/references/security.md
new file mode 100644
index 000000000..1abec790b
--- /dev/null
+++ b/.agents/skills/homarr-custom-widget/references/security.md
@@ -0,0 +1,7 @@
+# Security
+
+All requests use Homarr's protected server executor. Source origin, network scope, DNS, redirects, SSRF, rate limits, permissions, size limits, timeouts, and encrypted credential injection remain enforced.
+
+The JSX interpreter blocks imports, hooks, refs, raw event callbacks, browser requests, eval, arbitrary functions, prototype access, unsafe URLs, global CSS escape, arbitrary portals, bigint, statement blocks, IIFEs, and recursion. Regex literals must be bounded and reject backreferences, lookbehind, nested quantifiers, excessive length, and unsupported flags.
+
+Credentials are stored separately and never exported or returned to an agent. A published self-hosted source URL is only a suggestion: installers must confirm or replace private and loopback URLs for their own Homarr deployment. Source origins cannot be controlled through widget options.
diff --git a/.dockerignore b/.dockerignore
index 521c55109..c0f2129a3 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -17,7 +17,8 @@ coverage
dev
.build
e2e
-apps/docs
+apps/docs/*
+!apps/docs/package.json
apps/nextjs/.next
.env
.env.*
diff --git a/.env.example b/.env.example
index 22dca3adb..997d60865 100644
--- a/.env.example
+++ b/.env.example
@@ -61,9 +61,30 @@ TURBO_TELEMETRY_DISABLED=1
# Enable mock integration
UNSAFE_ENABLE_MOCK_INTEGRATION=true
+# Public URL contract. Values must be absolute HTTP(S) URLs without credentials,
+# query strings, or fragments. Trailing slashes are normalized at startup.
+HOMARR_WEBSITE_URL=https://homarr.dev
+# PocketBase API used by Homarr and by the standalone documentation site.
+# Defaults to HOMARR_WEBSITE_URL.
+# WORKSHOP_API_URL=https://homarr.dev
+# Public Workshop page opened from Homarr. Defaults to HOMARR_WEBSITE_URL/workshop.
+# WORKSHOP_WEB_URL=https://homarr.dev/workshop
+
+# Documentation and PocketBase deployment settings.
+# WORKSHOP_PUBLIC_ORIGIN=https://homarr.dev
+# PB_ALLOWED_ORIGINS=*
+
+# Local Workshop stack:
+# HOMARR_WEBSITE_URL=http://127.0.0.1:3003
+# WORKSHOP_API_URL=http://127.0.0.1:8090
+# WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop
+# WORKSHOP_PUBLIC_ORIGIN=http://127.0.0.1:3003
+# Optional: GITHUB_CLIENT_ID=
+# Optional: GITHUB_CLIENT_SECRET=
+
# Enable demo mode (seeds a demo user "demo"/"demo", a sample board and a mock integration)
# DEMO_MODE=true
# Whether demo mode is read-only. Defaults to true (the public demo blocks all mutations).
# Set to false for writable preview deployments so the demo user can modify the dashboard.
-# DEMO_READ_ONLY=false
\ No newline at end of file
+# DEMO_READ_ONLY=false
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 0d8b33a33..d4b730726 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -33,6 +33,7 @@ body:
options:
# The below comment is used to insert a new version with on-release.yml
#NEXT_VERSION#
+ - 1.74.0
- 1.73.0
- 1.72.0
- 1.71.0
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index ed325c9a8..d227e66d7 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -16,6 +16,6 @@
**When contributing a new integration or widget:**
- [ ] I confirmed manually that the integration or widget is working with a real system and performed basic smoke
- tests (e.g. what happens if it is offline)
+ tests (e.g. what happens if it is offline)
- [ ] Optional but recommended: I confirm that I am willing to be added to the `CODEOWNERS` of this feature and will
- provide support and maintain the integration (e.g. in case of breaking or failure)
\ No newline at end of file
+ provide support and maintain the integration (e.g. in case of breaking or failure)
diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml
index 026d45cb6..733e21eb9 100644
--- a/.github/workflows/autofix.yml
+++ b/.github/workflows/autofix.yml
@@ -1,4 +1,4 @@
-name: Auto-fix lint and format
+name: Formatting
on:
pull_request:
@@ -6,42 +6,30 @@ on:
branches: ["*"]
permissions:
- contents: write
+ contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
cancel-in-progress: true
jobs:
- autofix:
- name: Auto-fix
- if: github.event.pull_request.head.repo.id == github.repository_id
+ format:
+ name: Check formatting
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- ref: ${{ github.head_ref }}
+ persist-credentials: false
- name: Setup
uses: ./tooling/github/setup
- - name: Get changed files
- id: changed
+ - name: Check changed files
env:
BASE_REF: ${{ github.base_ref }}
run: |
- git diff --name-only --diff-filter=ACMR "origin/${BASE_REF}...HEAD" -- '*.ts' '*.tsx' '*.js' '*.jsx' > /tmp/changed-files
- echo "count=$(wc -l < /tmp/changed-files | tr -d '[:space:]')" >> $GITHUB_OUTPUT
-
- - name: Auto-fix lint + format
- if: steps.changed.outputs.count != '0'
- run: |
- xargs -r -a /tmp/changed-files pnpm exec oxlint --fix || true
- xargs -r -a /tmp/changed-files pnpm exec oxfmt --write
-
- - name: Commit fixes
- uses: stefanzweifel/git-auto-commit-action@v7
- with:
- commit_message: "style: auto-fix lint and format [skip ci]"
+ git diff --name-only --diff-filter=ACMR -z "origin/${BASE_REF}...HEAD" -- \
+ '*.css' '*.js' '*.json' '*.jsonc' '*.jsx' '*.md' '*.mdx' '*.scss' '*.ts' '*.tsx' '*.yaml' '*.yml' \
+ | xargs -0 -r pnpm exec oxfmt --check --
diff --git a/.github/workflows/automatic-approval.yml b/.github/workflows/automatic-approval.yml
deleted file mode 100644
index 9e246f838..000000000
--- a/.github/workflows/automatic-approval.yml
+++ /dev/null
@@ -1,35 +0,0 @@
-name: "[Dependency Updates] Auto Approve"
-on:
- pull_request:
- types: [opened, synchronize]
-
-permissions: {}
-
-jobs:
- approve-automatic-prs:
- runs-on: ubuntu-latest
- timeout-minutes: 2
- # 158783068: homarr-renovate[bot]
- # 190541745: homarr-crowdin[bot]
- # 210161987: homarr-update-contributors[bot]
- # 175486441: homarr-releases[bot]
- # Extracted from https://api.github.com/users/
- if: github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 || github.actor_id == 175486441
- steps:
- - name: Checkout code
- uses: actions/checkout@v7
- - name: Obtain token
- id: obtainToken
- uses: actions/create-github-app-token@v3
- with:
- private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
- client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
- permission-pull-requests: write # required to approve pull request
-
- - name: Install GitHub CLI
- run: sudo apt-get install -y gh
- - name: Approve automatic PRs
- env:
- GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
- run: |
- gh pr review ${{github.event.pull_request.number}} --approve --body "Automatically approved by GitHub Action"
diff --git a/.github/workflows/bot-auto-merge-reconcile.yml b/.github/workflows/bot-auto-merge-reconcile.yml
new file mode 100644
index 000000000..d82f759f5
--- /dev/null
+++ b/.github/workflows/bot-auto-merge-reconcile.yml
@@ -0,0 +1,43 @@
+name: "[Bots] Reconcile Auto-merge"
+
+on:
+ schedule:
+ - cron: "*/30 * * * *"
+ workflow_dispatch:
+
+permissions: {}
+
+jobs:
+ reconcile:
+ runs-on: ubuntu-latest
+ timeout-minutes: 15
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v7
+
+ - name: Obtain token
+ id: obtainToken
+ uses: actions/create-github-app-token@v3
+ with:
+ private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
+ client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
+ permission-pull-requests: write # required to enable auto-merge
+
+ - name: Install GitHub CLI
+ run: sudo apt-get install -y gh
+
+ - name: Re-enable auto-merge for prepared bot PRs
+ env:
+ GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
+ run: |
+ # 158783068: homarr-renovate[bot]
+ # 190541745: homarr-crowdin[bot]
+ # 210161987: homarr-update-contributors[bot]
+ # 175441: homarr-releases[bot]
+ gh pr list --repo "${{ github.repository }}" --base dev --state open --limit 100 \
+ --json number,author,reviewDecision,mergeStateStatus,autoMergeRequest \
+ --jq '.[] | select(.author.login as $login | ["app/homarr-renovate", "app/homarr-crowdin", "app/homarr-update-contributors", "app/homarr-releases"] | index($login)) | select(.reviewDecision == "APPROVED") | select(.mergeStateStatus == "CLEAN") | select(.autoMergeRequest == null) | .number' \
+ | while read -r pr; do
+ echo "Re-enabling auto-merge on PR #$pr"
+ gh pr merge "$pr" --auto --squash
+ done
diff --git a/.github/workflows/bot-pr-automerge.yml b/.github/workflows/bot-pr-automerge.yml
new file mode 100644
index 000000000..35c5461fa
--- /dev/null
+++ b/.github/workflows/bot-pr-automerge.yml
@@ -0,0 +1,47 @@
+name: "[Bots] Approve and Auto-merge"
+
+on:
+ pull_request:
+ types: [opened, synchronize, reopened, ready_for_review]
+
+permissions: {}
+
+jobs:
+ approve-and-automerge:
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ # 158783068: homarr-renovate[bot]
+ # 190541745: homarr-crowdin[bot]
+ # 210161987: homarr-update-contributors[bot]
+ # 175441: homarr-releases[bot]
+ # Extracted from https://api.github.com/users/
+ if: |
+ (github.actor_id == 158783068 || github.actor_id == 190541745 || github.actor_id == 210161987 || github.actor_id == 175441) &&
+ github.event.pull_request.head.repo.full_name == github.repository &&
+ github.event.pull_request.base.ref == 'dev'
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v7
+
+ - name: Obtain token
+ id: obtainToken
+ uses: actions/create-github-app-token@v3
+ with:
+ private-key: ${{ secrets.RENOVATE_APPROVE_PRIVATE_KEY }}
+ client-id: ${{ secrets.RENOVATE_APPROVE_APP_ID }}
+ permission-pull-requests: write # required to approve a pull request and enable auto-merge
+
+ - name: Install GitHub CLI
+ run: sudo apt-get install -y gh
+
+ - name: Approve and enable auto-merge
+ env:
+ GITHUB_TOKEN: ${{ steps.obtainToken.outputs.token }}
+ run: |
+ PR_NUMBER="${{ github.event.pull_request.number }}"
+ if [ "$(gh pr view "$PR_NUMBER" --json reviewDecision --jq .reviewDecision)" != "APPROVED" ]; then
+ gh pr review "$PR_NUMBER" --approve --body "Automatically approved by Homarr bot policy"
+ fi
+ if [ "$(gh pr view "$PR_NUMBER" --json autoMergeRequest --jq '.autoMergeRequest != null')" != "true" ]; then
+ gh pr merge "$PR_NUMBER" --auto --squash
+ fi
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index addcf5e7d..643c4f436 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -8,7 +8,7 @@ on:
types: [closed]
branches: ["*"]
push:
- branches: [main, dev, beta, next]
+ branches: [main, dev, beta, next, v2]
merge_group:
workflow_dispatch:
inputs:
@@ -34,8 +34,10 @@ permissions:
contents: read
concurrency:
- group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || inputs.pr_number || github.ref }}
- cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
+ # Every event for a PR shares one group so a close event cancels stale image
+ # publication and a newer synchronize run cannot be overwritten by an older run.
+ group: ${{ github.workflow }}-${{ github.event.pull_request.number || inputs.pr_number || github.ref }}
+ cancel-in-progress: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' || github.event_name == 'workflow_dispatch' || github.ref != 'refs/heads/main' }}
env:
FORCE_COLOR: 3
@@ -82,11 +84,22 @@ jobs:
run: cp .env.example .env
- name: Lint, typecheck, and build affected workspaces
- run: pnpm turbo run lint typecheck build --affected --continue=dependencies-successful --concurrency=4
+ run: pnpm turbo run lint typecheck build --affected --continue=dependencies-successful --concurrency=2
- name: Validate workspace configuration
run: pnpm lint:ws
+ - name: Validate Custom Widget architecture
+ run: pnpm check:custom-widgets
+
+ - name: Validate checked-in OpenAPI schema
+ run: pnpm check:openapi
+
+ - name: Validate Custom Widget bundle budget
+ run: |
+ pnpm --filter nextjs build
+ pnpm check:custom-widget-bundle
+
- name: Run unit tests
run: pnpm test
@@ -102,6 +115,8 @@ jobs:
permissions:
contents: read
packages: write
+ outputs:
+ digest: ${{ steps.preview-build.outputs.digest }}
steps:
- uses: actions/checkout@v7
if: "!env.ACT"
@@ -184,38 +199,166 @@ jobs:
path: e2e/test-results/
retention-days: 7
- - name: Determine preview tag
- id: preview
+ - name: Log in to GHCR for preview publication
if: |
!env.ACT &&
(
(github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
)
- env:
- PR_NUMBER: ${{ github.event.pull_request.number || inputs.pr_number }}
- run: echo "tag=pr-${PR_NUMBER}" >> "$GITHUB_OUTPUT"
-
- - name: Log in to GHCR
- if: steps.preview.outputs.tag != ''
uses: docker/login-action@v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- - name: Push preview image
- if: steps.preview.outputs.tag != ''
- env:
- PREVIEW_TAG: ${{ steps.preview.outputs.tag }}
- run: |
- docker tag homarr-ci "${PREVIEW_IMAGE}:${PREVIEW_TAG}"
- docker push "${PREVIEW_IMAGE}:${PREVIEW_TAG}"
+ - name: Push amd64 preview image by digest
+ id: preview-build
+ if: |
+ !env.ACT &&
+ (
+ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
+ (github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
+ )
+ uses: docker/build-push-action@v7
+ with:
+ context: .
+ network: host
+ platforms: linux/amd64
+ outputs: type=image,"name=${{ env.PREVIEW_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
+ cache-from: type=gha,scope=homarr-linux-amd64
+ secrets: |
+ TURBO_API=${{ secrets.TURBO_API }}
+ TURBO_TEAM=${{ secrets.TURBO_TEAM }}
+ TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
+ TURBO_REMOTE_CACHE_SIGNATURE_KEY=${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
- name: Show Act publication result
if: env.ACT
run: echo "Act validated the publication path; external GHCR writes are disabled locally."
+ preview-arm64:
+ name: Build arm64 preview image
+ needs: fast-gate
+ if: |
+ (
+ (github.event_name == 'pull_request' && github.event.pull_request.draft == false && github.event.pull_request.head.repo.full_name == github.repository) ||
+ (github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
+ )
+ runs-on: ubuntu-24.04-arm
+ timeout-minutes: 20
+ permissions:
+ contents: read
+ packages: write
+ outputs:
+ digest: ${{ steps.preview-build.outputs.digest }}
+ steps:
+ - uses: actions/checkout@v7
+ if: "!env.ACT"
+ with:
+ persist-credentials: false
+ ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || '' }}
+
+ - name: Log in to GHCR
+ if: "!env.ACT"
+ uses: docker/login-action@v4
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Set up Docker Buildx
+ if: "!env.ACT"
+ uses: docker/setup-buildx-action@v4
+
+ - name: Build and push arm64 preview image by digest
+ id: preview-build
+ if: "!env.ACT"
+ uses: docker/build-push-action@v7
+ with:
+ context: .
+ network: host
+ platforms: linux/arm64
+ outputs: type=image,"name=${{ env.PREVIEW_IMAGE }}",push-by-digest=true,name-canonical=true,push=true
+ cache-from: type=gha,scope=homarr-linux-arm64
+ cache-to: type=gha,scope=homarr-linux-arm64,mode=max
+ secrets: |
+ TURBO_API=${{ secrets.TURBO_API }}
+ TURBO_TEAM=${{ secrets.TURBO_TEAM }}
+ TURBO_TOKEN=${{ secrets.TURBO_TOKEN }}
+ TURBO_REMOTE_CACHE_SIGNATURE_KEY=${{ secrets.TURBO_REMOTE_CACHE_SIGNATURE_KEY }}
+
+ publish-preview:
+ name: Publish multi-platform preview image
+ needs: [container, preview-arm64]
+ if: |
+ (
+ (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository) ||
+ (github.event_name == 'workflow_dispatch' && inputs.operation == 'publish')
+ )
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ permissions:
+ contents: read
+ packages: write
+ pull-requests: read
+ env:
+ PREVIEW_TAG: pr-${{ github.event.pull_request.number || inputs.pr_number }}
+ steps:
+ - name: Log in to GHCR
+ if: "!env.ACT"
+ uses: docker/login-action@v4
+ with:
+ registry: ${{ env.REGISTRY }}
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Set up Docker Buildx
+ if: "!env.ACT"
+ uses: docker/setup-buildx-action@v4
+
+ - name: Verify PR head before publication
+ if: github.event_name == 'pull_request'
+ uses: actions/github-script@v8
+ env:
+ EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
+ with:
+ script: |
+ const { data: pull } = await github.rest.pulls.get({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ pull_number: context.payload.pull_request.number,
+ });
+ if (pull.state !== "open") {
+ core.setFailed(`PR #${pull.number} is ${pull.state}; refusing to publish its preview.`);
+ return;
+ }
+ if (pull.head.sha !== process.env.EXPECTED_HEAD_SHA) {
+ core.setFailed(`PR #${pull.number} moved from ${process.env.EXPECTED_HEAD_SHA} to ${pull.head.sha}.`);
+ }
+
+ - name: Publish preview manifest
+ if: "!env.ACT"
+ env:
+ AMD64_DIGEST: ${{ needs.container.outputs.digest }}
+ ARM64_DIGEST: ${{ needs.preview-arm64.outputs.digest }}
+ run: |
+ docker buildx imagetools create -t "${PREVIEW_IMAGE}:${PREVIEW_TAG}" \
+ "${PREVIEW_IMAGE}@${AMD64_DIGEST}" \
+ "${PREVIEW_IMAGE}@${ARM64_DIGEST}"
+
+ - name: Verify preview manifest platforms
+ if: "!env.ACT"
+ run: |
+ image="${PREVIEW_IMAGE}:${PREVIEW_TAG}"
+ manifest="$(docker buildx imagetools inspect "$image" --raw)"
+ for architecture in amd64 arm64; do
+ jq --exit-status --arg architecture "$architecture" \
+ 'any(.manifests[]; .platform.os == "linux" and .platform.architecture == $architecture)' \
+ <<<"$manifest" >/dev/null
+ done
+ docker buildx imagetools inspect "$image"
+
cleanup:
name: Clean up preview image
if: github.event_name == 'pull_request_target' || (github.event_name == 'workflow_dispatch' && inputs.operation == 'cleanup')
diff --git a/.github/workflows/workshop.yml b/.github/workflows/workshop.yml
new file mode 100644
index 000000000..658fc7985
--- /dev/null
+++ b/.github/workflows/workshop.yml
@@ -0,0 +1,63 @@
+name: Workshop
+
+on:
+ pull_request:
+ push:
+ branches: [dev, v2, main]
+
+permissions:
+ contents: read
+
+jobs:
+ validate:
+ runs-on: ubuntu-24.04
+ timeout-minutes: 30
+ steps:
+ - uses: actions/checkout@v7
+ - uses: actions/setup-node@v6
+ with:
+ node-version: 24.18.0
+ - uses: pnpm/action-setup@v6
+ with:
+ version: 11.11.0
+ run_install: false
+ - run: pnpm install --frozen-lockfile
+ - run: pnpm --filter @homarr/workshop typecheck
+ - run: pnpm --filter @homarr/docs typecheck
+ - run: pnpm test:workshop
+ - run: docker build --target production -f apps/workshop/Dockerfile .
+
+ publish:
+ if: github.event_name == 'push' && (github.ref_name == 'v2' || github.ref_name == 'main')
+ needs: validate
+ runs-on: ubuntu-24.04
+ timeout-minutes: 30
+ permissions:
+ contents: read
+ packages: write
+ steps:
+ - uses: actions/checkout@v7
+ - uses: docker/setup-qemu-action@v4
+ - uses: docker/setup-buildx-action@v4
+ - uses: docker/login-action@v4
+ with:
+ registry: ghcr.io
+ username: ${{ github.actor }}
+ password: ${{ secrets.GITHUB_TOKEN }}
+ - uses: docker/metadata-action@v6
+ id: metadata
+ with:
+ images: ghcr.io/homarr-labs/workshop
+ tags: |
+ type=sha,prefix=sha-,format=long
+ type=raw,value=v2,enable=${{ github.ref_name == 'v2' }}
+ type=raw,value=latest,enable=${{ github.ref_name == 'main' }}
+ - uses: docker/build-push-action@v7
+ with:
+ context: .
+ file: apps/workshop/Dockerfile
+ target: production
+ platforms: linux/amd64,linux/arm64
+ push: true
+ tags: ${{ steps.metadata.outputs.tags }}
+ labels: ${{ steps.metadata.outputs.labels }}
diff --git a/.gitignore b/.gitignore
index c22262f6d..82e02993b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ out/
next-env.d.ts
# artifacts
+.ai-evaluations/
packages/db/migrations/*/migrate.cjs
# nitro
@@ -71,10 +72,15 @@ apps/nextjs/public/images/background.png
# personal agent config
.opencode
-.agents/
+.agents/*
+!.agents/skills/
+.agents/skills/*
+!.agents/skills/homarr-custom-widget/
+!.agents/skills/homarr-custom-widget/**
skills-lock.json
# docusaurus
apps/docs/.docusaurus
apps/docs/build
.pnpm-store
+benchmark-results
diff --git a/.impeccable/critique/2026-07-21T11-32-50Z__apps-docs-src-components-workshop.md b/.impeccable/critique/2026-07-21T11-32-50Z__apps-docs-src-components-workshop.md
new file mode 100644
index 000000000..7ea4baa64
--- /dev/null
+++ b/.impeccable/critique/2026-07-21T11-32-50Z__apps-docs-src-components-workshop.md
@@ -0,0 +1,116 @@
+---
+target: Workshop store in the docs
+total_score: 20
+p0_count: 1
+p1_count: 3
+timestamp: 2026-07-21T11-32-50Z
+slug: apps-docs-src-components-workshop
+---
+
+# Workshop Store Critique
+
+## Design Health Score
+
+| # | Heuristic | Score | Key issue |
+| --------- | ----------------------------------- | --------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1 | Visibility of system status | 1/4 | Fetch failure can show beside an empty-store message, so the system communicates two incompatible states. |
+| 2 | Match between system and real world | 3/4 | Widget, CSS, voting, revisions, and comments use familiar language. |
+| 3 | User control and freedom | 2/4 | Filters are reversible, but the crashed route and failed fetch state offer no useful recovery. |
+| 4 | Consistency and standards | 2/4 | The submission editor uses CodeMirror, while revision editing falls back to a textarea; several tiny custom controls do not follow the shared button vocabulary. |
+| 5 | Error prevention | 2/4 | Submission validation exists, but disabled progression does not explain what is missing and destructive actions use a brief inline confirmation. |
+| 6 | Recognition rather than recall | 3/4 | Labels, type choices, and the submission stepper are understandable without documentation. |
+| 7 | Flexibility and efficiency | 2/4 | Search covers titles only, signed-out users can select Yours, and installation is reduced to ambiguous Copy and Download actions. |
+| 8 | Aesthetic and minimalist design | 3/4 | The restrained visual system is clean, but the browse toolbar and card action rows become dense. |
+| 9 | Help users recover from errors | 0/4 | The production route crashes with React error 130; the fetch error has no retry action or useful context. |
+| 10 | Help and documentation | 2/4 | The editor explains its fields, but the store does not teach the full inspect, trust, download, and install journey. |
+| **Total** | | **20/40** | **Needs work before release** |
+
+## Anti-Patterns Verdict
+
+The surface does not look like obvious AI-generated decoration. Its visual restraint, familiar controls, and practical editor are credible product UI. The weakness is product behavior rather than styling: contradictory states, tiny action controls, an overloaded responsive toolbar, and a primary installation journey that is not explicit.
+
+The deterministic Impeccable scan returned zero findings for `apps/docs/src/components/workshop`. This is useful but incomplete. The detector did not catch runtime failures, semantic state conflicts, touch-target sizing, or responsive grouping. Browser evidence found a full route crash, React error 130, and an earlier state that rendered both `Something went wrong.` and `No submissions yet`.
+
+No visual overlay was injected because the available browser evaluation surface is read-only. Screenshots, DOM snapshots, console logs, and source inspection were used instead.
+
+## Overall Impression
+
+The submission modal is now one of the stronger parts of the Workshop. The store around it is not release-ready. The single biggest opportunity is to make discovery and installation a dependable, explicit path: load reliably, show one truthful state, help users evaluate a submission, and give them one obvious next action.
+
+## What Is Working
+
+1. The visual system is restrained and consistent with the Homarr documentation. Typography, color, radii, and spacing do not compete with the content.
+2. The submission flow chunks a complex task into Type, Details, and Media. The shared JSON/CSS editor, drag-and-drop import, metadata autofill, and capped media previews reduce creator effort.
+3. The detail design exposes revision, author, reports, changelog, source content, and comments. This supports inspection rather than treating community code as a blind install.
+
+## Priority Issues
+
+### P0: The Workshop route crashes in the production docs build
+
+**Why it matters:** No visual polish matters if the store becomes a Docusaurus error page. A fresh independent tab consistently reached `This page crashed` with React error 130. The console attributed the thrown error to the Kapa widget bundle.
+
+**Fix:** Isolate or disable the Kapa script on Workshop routes, prevent third-party widget failures from reaching the Docusaurus route boundary, and add a route-level Workshop boundary that preserves navigation and retry. Reproduce against the built Docker docs image, not only the development server.
+
+**Suggested command:** `$impeccable harden`
+
+### P1: Failure and empty states contradict each other
+
+**Why it matters:** Showing `Something went wrong.` followed by `No submissions yet` makes users believe the store is both broken and empty. The generic message gives no cause and no recovery.
+
+**Fix:** Make loading, failure, empty, and results mutually exclusive. The failure state should say that listings could not be loaded, preserve the current filters, and provide `Try loading again`. Do not render filters or an empty store as authoritative data after the initial request fails.
+
+**Suggested command:** `$impeccable harden`
+
+### P1: Mobile discovery controls are too dense and do not form a responsive hierarchy
+
+**Why it matters:** The toolbar's inner row contains four type choices, sorting, and outdated visibility without wrapping. Most controls are 24 to 28 pixels tall. At narrow widths this is difficult to scan, easy to clip, and below a comfortable touch target.
+
+**Fix:** Put a full-width search first on mobile, keep All, Widgets, and CSS as the primary segmented choice, move sorting and outdated visibility into a compact secondary row or popover, and hide Yours when signed out or make it a sign-in affordance. Use at least 40 to 44 pixel touch targets. Add `aria-pressed` or proper tab semantics to the type filter.
+
+**Suggested command:** `$impeccable adapt`
+
+### P1: The store does not make the install journey obvious
+
+**Why it matters:** A first-time user sees Copy and Download without knowing which is recommended, what will be copied, how to inspect safety, or where the file goes in Homarr. This weakens the store's main purpose.
+
+**Fix:** Make the detail page the decision surface. Use one primary action named for its result, such as `Download widget JSON` or `Copy CSS`, with a short `Install in Homarr` explanation linked to the correct in-app flow. Cards should prioritize title, preview, compatibility, score, and `View details`; secondary utilities should not compete with evaluation.
+
+**Suggested command:** `$impeccable clarify`
+
+### P2: Card and moderation actions are too small and too numerous
+
+**Why it matters:** Vote, copy, download, comment, delete, and report controls compete in a compact footer. Low-contrast icon buttons are hard to discover and hard to use by touch. CSS columns also create a top-to-bottom visual reading order that can differ from the expected row order.
+
+**Fix:** Use a regular responsive grid, remove hover scaling, keep one or two visible card actions, and move owner/moderation actions to a menu. Keep voting visible but increase its target size. Correct the report copy: reports and reporter identities are moderation data, not public social proof.
+
+**Suggested command:** `$impeccable distill`
+
+## Persona Red Flags
+
+### Jordan, first-time self-hoster
+
+Jordan opens Workshop to install a widget. The route can crash before any listing appears. In the partial failure state, Jordan sees both an error and an empty store. If listings load, Copy and Download do not explain the Homarr destination or recommended path. Jordan is likely to leave before installing anything.
+
+### Alex, experienced Homarr administrator
+
+Alex can understand the JSON and appreciates the source viewer, but title-only search and limited filtering make a larger catalog inefficient. Compatibility and required secret information are not prominent at browse time. Tiny moderation and owner controls slow repeated management tasks.
+
+### Morgan, mobile user
+
+Morgan encounters a toolbar with more controls than fit comfortably in one decision group, 24 to 28 pixel targets, and a signed-out Yours filter that leads to an unhelpful no-results state. Submission cards add another dense row of small actions.
+
+## Minor Observations
+
+- Future submission steps are focusable buttons even when clicking them does nothing. Disable them or render non-interactive progress markers.
+- The report textarea relies on placeholder text and needs a persistent accessible label.
+- The revision editor should reuse the same JSON/CSS CodeMirror component as creation.
+- Search should include title, description, and author at minimum.
+- The timed inline `Confirm?` deletion pattern is easy to miss and weak for keyboard and touch users.
+- The detail-page changelog uses an uppercase tracked label, which is unnecessary in this otherwise restrained interface.
+- Loading comments should use the same skeleton vocabulary as listings rather than centered status text.
+
+## Questions to Consider
+
+1. Is Workshop primarily a catalog for discovery, or a technical exchange for copying source? The primary card and detail actions should commit to one answer.
+2. Should users evaluate compatibility and required secrets before opening a listing, or only on the detail page?
+3. Does Workshop need every moderation and ownership action visible inline, or should those actions live behind a menu?
diff --git a/.impeccable/critique/2026-07-21T12-29-41Z__apps-docs-src-components-workshop.md b/.impeccable/critique/2026-07-21T12-29-41Z__apps-docs-src-components-workshop.md
new file mode 100644
index 000000000..0021929a4
--- /dev/null
+++ b/.impeccable/critique/2026-07-21T12-29-41Z__apps-docs-src-components-workshop.md
@@ -0,0 +1,64 @@
+---
+target: Workshop store in the docs
+total_score: 38
+p0_count: 0
+p1_count: 0
+timestamp: 2026-07-21T12-29-41Z
+slug: apps-docs-src-components-workshop
+---
+
+# Workshop Store Critique — Post-Polish
+
+## Design Health Score
+
+| # | Heuristic | Score | Evidence |
+| --------- | ----------------------------------- | --------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1 | Visibility of system status | 4/4 | Loading, initial failure, stale refresh failure, empty, and results states are mutually exclusive; recovery actions are visible. |
+| 2 | Match between system and real world | 4/4 | Actions describe their result: Download widget JSON, Copy CSS, View details, and Install in Homarr. |
+| 3 | User control and freedom | 4/4 | Filters can be cleared, errors retried, dialogs cancelled, and destructive submission deletion requires explicit confirmation. |
+| 4 | Consistency and standards | 4/4 | Creation and revision editing share CodeMirror, buttons follow the shared system, and cards use a predictable responsive grid. |
+| 5 | Error prevention | 3/4 | Validation, disabled progression, pending states, RLS, and confirmations are strong; disabled form progression could explain unmet requirements more explicitly. |
+| 6 | Recognition rather than recall | 4/4 | Persistent labels, type descriptions, step status, source previews, and installation instructions minimize recall. |
+| 7 | Flexibility and efficiency | 4/4 | Search spans title, description, and author; signed-in filtering, sorting, responsive controls, and direct detail actions support repeat use. |
+| 8 | Aesthetic and minimalist design | 3/4 | The catalog is restrained and scannable; long source documents and the complete creator form necessarily remain dense on small screens. |
+| 9 | Help users recover from errors | 4/4 | Route, listing, edit, report, copy, OAuth, and detail failures remain local and provide a clear next action. |
+| 10 | Help and documentation | 4/4 | The detail page explains the exact Homarr destination and links to updated Workshop documentation. |
+| **Total** | | **38/40** | **Release-quality; minor polish remains** |
+
+## Anti-Patterns Verdict
+
+The surface is product-led rather than decorative. The redesign removes the overloaded card footer, hover scaling, tiny touch controls, public moderation details, contradictory states, and ambiguous install actions. The deterministic Impeccable detector reports zero findings.
+
+## What Improved
+
+1. The production route no longer collapses into the Docusaurus crash screen without recovery. A route-local boundary protects navigation, and the fresh-volume production path renders without console errors.
+2. Loading, failure, empty, stale-results, and populated states are truthful and mutually exclusive.
+3. Search and filters form a clear responsive hierarchy. Mobile targets are 40–44 pixels, signed-out users do not see Yours, and cards no longer overflow at 390 pixels.
+4. Cards prioritize evaluation: title, author, type, score, description, comments, and View details. Copy, download, reporting, editing, and deletion moved to the detail page.
+5. The detail page now teaches the installation journey and names its primary action precisely.
+6. Reports and reporter identities are admin-only through PocketBase rules. Public catalog UI no longer turns reports into social proof.
+7. The creator and revision dialogs share the CodeMirror editor, use bounded internal scrolling, expose pending/error states, and avoid step-selection layout shifts.
+
+## Remaining Minor Opportunities
+
+### P2: Explain disabled progression inline
+
+The Type and Details steps are understandable, but a disabled Next button could state the missing requirement for keyboard and first-time users. This is a refinement, not a blocker, because required fields and validation messages are already present.
+
+### P2: Consider a compact source summary on very small screens
+
+The read-only source viewer is intentionally complete and horizontally scrollable. A future collapse/expand summary could reduce page length for nontechnical visitors without hiding inspection entirely.
+
+## Verification Evidence
+
+- Fresh Docker Compose PocketBase volume and production docs image.
+- Populated catalog with widget, CSS, and outdated examples.
+- Desktop and 390px mobile layouts in light and dark themes.
+- Widget detail, installation instructions, CodeMirror source view, and comments.
+- Signed-in submission wizard through Type and Details, including the shared editor.
+- Zero browser console errors after the final fixes.
+- Impeccable deterministic detector: zero findings.
+
+## Compatibility Note
+
+An existing local development volume created from an earlier unreleased version of the reset migration still has a stale Workshop view. It is intentionally not migrated or deleted. A fresh volume created from the current unreleased migration is the supported validation target and works correctly.
diff --git a/.impeccable/critique/2026-07-22T15-58-15Z__apps-docs-src-components-workshop.md b/.impeccable/critique/2026-07-22T15-58-15Z__apps-docs-src-components-workshop.md
new file mode 100644
index 000000000..66884800a
--- /dev/null
+++ b/.impeccable/critique/2026-07-22T15-58-15Z__apps-docs-src-components-workshop.md
@@ -0,0 +1,106 @@
+---
+target: Homarr Workshop docs UI
+total_score: 27
+p0_count: 0
+p1_count: 2
+timestamp: 2026-07-22T15-58-15Z
+slug: apps-docs-src-components-workshop
+---
+
+## Design Health Score
+
+| # | Heuristic | Score | Key Issue |
+| --------- | ------------------------------- | --------: | --------------------------------------------------------------------------------------------------------------------------------------------- |
+| 1 | Visibility of System Status | 3 | Strong loading, retry, stale-data, pending, and copy states; successful report/edit actions still end silently. |
+| 2 | Match System / Real World | 3 | Core Workshop language is clear, but `networkScope`, query/action, and schema terminology assumes technical fluency. |
+| 3 | User Control and Freedom | 3 | Back, cancel, clear filters, vote toggling, and destructive confirmations are present; destructive actions have no undo. |
+| 4 | Consistency and Standards | 3 | Shared primitives are coherent, though Docusaurus chrome and the denser app-store vocabulary still feel slightly stitched together. |
+| 5 | Error Prevention | 2 | Validation and confirmations are good, but install safety and credential context appears after download/copy actions on small screens. |
+| 6 | Recognition Rather Than Recall | 3 | Author, revision, source, filters, and capabilities are visible; installation still asks users to remember a separate Homarr navigation path. |
+| 7 | Flexibility and Efficiency | 2 | Search, filters, sort, drag-and-drop, autofill, and comment shortcuts help; the install handoff remains manual. |
+| 8 | Aesthetic and Minimalist Design | 2 | Hierarchy is clear, but repeated panels, badges, uppercase metadata labels, and overlapping install actions add noise. |
+| 9 | Error Recognition and Recovery | 3 | Errors are plain-language and usually preserve work; the global action error can be detached from the failed control. |
+| 10 | Help and Documentation | 3 | The installation guide and source explanation are useful; security terminology lacks inline definitions. |
+| **Total** | | **27/40** | **Acceptable, significant improvements needed** |
+
+## Anti-Patterns Verdict
+
+**LLM assessment:** Moderate AI-assisted feel, not outright slop. Domain-specific copy, extensive state coverage, and transparent source/capability information make the feature credible. The generic card grid, repeated rounded panels, icon tiles, uppercase tracked labels, and decorative changelog stripe still expose a component-recipe grammar. It looks competent but not fully edited into a distinctive Homarr store.
+
+**Deterministic scan:** `detect.mjs --json apps/docs/src/components/workshop` exited 0 with exact output `[]`: zero findings, zero triggered rules, and no false positives. The scan found none of Impeccable's deterministic anti-patterns.
+
+**Visual overlays:** No reliable user-visible overlay is available. The independent detector agent had no browser backend. The parent fallback opened a fresh browser tab and visually inspected the listing, but the browser API did not expose a mutable evaluation method for the required preflight, so injection and `[Human]` visibility were correctly skipped. Browser/source evidence was used instead.
+
+## Overall Impression
+
+The Workshop is already substantially better than a generic gallery: it communicates authorship, source code, API capabilities, secrets, revision history, reports, and discussion. The biggest opportunity is to turn that information into a deliberate trust sequence. Today the interface lets the user act first and understand safety later, especially on mobile.
+
+## What's Working
+
+- **State coverage is unusually strong.** Listing failures, stale data, loading skeletons, empty results, filter recovery, comment errors, pending actions, and destructive confirmations have explicit UI.
+- **Pre-install transparency is excellent.** API hosts, methods, configurable options, network scope, and credential requirements are visible instead of hidden behind a download.
+- **The new primitive layer is coherent.** Search, filters, menus, switches, dialogs, and code presentation share one restrained interaction vocabulary across light and dark themes.
+
+## Priority Issues
+
+### [P1] Safety context arrives after the install action on mobile
+
+**Why it matters:** Download and copy actions appear in the header, while source capabilities, credential requirements, and installation guidance live in an aside that drops below the long source and discussion sections under the desktop breakpoint. A first-time mobile user can commit before understanding what the widget contacts or what secrets it needs.
+
+**Fix:** Put a compact “Before you install” summary immediately below the header on small screens: API hosts, network scopes, credential count, and action/query counts. Keep the full sticky details panel on desktop.
+
+**Suggested command:** `$impeccable layout`
+
+### [P1] Primary button and own-comment contrast misses AA
+
+**Why it matters:** The requested theme's primary red with white foreground is about 3.4:1, below 4.5:1 for small text. The same pairing affects primary buttons and the author's comment bubbles.
+
+**Fix:** Darken the primary red or use a dark foreground in light mode, then verify default buttons and comment bubbles in both themes.
+
+**Suggested command:** `$impeccable colorize`
+
+### [P2] The listing's server-rendered fallback is blank
+
+**Why it matters:** Before hydration, the listing renders an empty block instead of preserving the page's hierarchy and indicating progress. This creates a visible dead zone on slower self-hosted deployments.
+
+**Fix:** Render the listing heading, toolbar silhouette, and shared skeleton cards as the `BrowserOnly` fallback, matching the detail page's loading treatment.
+
+**Suggested command:** `$impeccable harden`
+
+### [P2] Download, copy, source, and install compete for the same task
+
+**Why it matters:** “Download widget,” “Copy JSON,” a full source viewer, and a separate installation panel all appear as parallel paths. Users must infer the recommended workflow.
+
+**Fix:** Make “Download widget JSON” the single primary header action. Keep copy beside the source viewer and place the next import step directly beside or immediately after download.
+
+**Suggested command:** `$impeccable clarify`
+
+### [P2] Repeated metadata scaffolding creates visual noise
+
+**Why it matters:** Card shells, badges, icon tiles, uppercase tracked labels, and the changelog stripe make the detail page feel assembled from recipes and slow scanning.
+
+**Fix:** Flatten the widget details into a compact definition list, reserve badges for status, and remove the decorative changelog stripe.
+
+**Suggested command:** `$impeccable distill`
+
+## Persona Red Flags
+
+**Jordan, first-timer:** “Network scope,” queries, actions, options, and the manifest schema appear without inline explanation. Download versus copy has no stated recommendation. On mobile, credential and installation context arrives after source and discussion.
+
+**Sam, accessibility-dependent user:** Primary controls and author comment bubbles miss small-text contrast. Copy, optimistic votes, filter result changes, and successful report/edit completion do not expose explicit live-region behavior. Gallery controls and dots are below comfortable touch-target sizes.
+
+**Casey, distracted mobile user:** The most important safety panel falls to the bottom, while primary actions stay at the top. Long source and discussion content separate the decision from its explanation. The submit type selector remains dense at narrow widths.
+
+## Minor Observations
+
+- The internal installation guide uses an external-link icon.
+- The final publication action says “Submit” rather than “Publish submission.”
+- Dark-theme tokens are declared in two blocks, increasing drift risk.
+- Screenshot dots rely on opacity alone and lack `aria-current`.
+- Successful report and edit actions deserve a small confirmation instead of silent dismissal.
+
+## Questions to Consider
+
+- If source review is important enough to say “Review exactly what will be installed,” why can users download before seeing the capability and credential summary?
+- Is Workshop primarily a code catalog or a safety-conscious app store? The current action hierarchy tries to be both.
+- Should the successful end state be a downloaded file, or a confident handoff into Homarr with the next step already visible?
diff --git a/.oxfmtrc.json b/.oxfmtrc.json
index dfe3385bd..c5532ca72 100644
--- a/.oxfmtrc.json
+++ b/.oxfmtrc.json
@@ -3,5 +3,15 @@
"printWidth": 120,
"endOfLine": "lf",
"sortPackageJson": true,
- "ignorePatterns": ["**/*.cjs", "**/lang/**", "static-data/**", "CHANGELOG.md", "apps/docs/.docusaurus/**", "apps/docs/build/**", "apps/docs/blog/**"]
+ "ignorePatterns": [
+ "**/*.cjs",
+ "**/lang/**",
+ "static-data/**",
+ "CHANGELOG.md",
+ "apps/docs/.docusaurus/**",
+ "apps/docs/build/**",
+ "apps/docs/blog/**",
+ "packages/custom-widgets/src/core/component-catalog.generated.json",
+ "apps/docs/static/custom-widgets/component-catalog-v1.json"
+ ]
}
diff --git a/.oxlintrc.json b/.oxlintrc.json
index 946ca3e6c..8f0096195 100644
--- a/.oxlintrc.json
+++ b/.oxlintrc.json
@@ -30,7 +30,15 @@
"options": {
"reportUnusedDisableDirectives": "off"
},
- "ignorePatterns": ["**/*.config.js", "**/*.config.mjs", "**/*.cjs", "**/lang/*.d.json.ts", ".next/**", "apps/docs/.docusaurus/**", "apps/docs/build/**"],
+ "ignorePatterns": [
+ "**/*.config.js",
+ "**/*.config.mjs",
+ "**/*.cjs",
+ "**/lang/*.d.json.ts",
+ ".next/**",
+ "apps/docs/.docusaurus/**",
+ "apps/docs/build/**"
+ ],
"settings": {
"react": { "version": "19" }
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f89968fdb..a829e2042 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,5 @@
+## [1.74.0](https://github.com/homarr-labs/homarr/compare/v1.73.0...v1.74.0) (2026-08-07)
+
## [1.73.0](https://github.com/homarr-labs/homarr/compare/v1.72.0...v1.73.0) (2026-07-31)
## [1.72.0](https://github.com/homarr-labs/homarr/compare/v1.71.0...v1.72.0) (2026-07-24)
diff --git a/Dockerfile b/Dockerfile
index 526f353da..f07fcae3b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,12 +3,18 @@
FROM node:24.18.0-alpine AS base
FROM base AS builder
+ARG TARGETPLATFORM
WORKDIR /app
-RUN apk add --no-cache libc6-compat curl bash && apk update
+# Native dependencies normally use prebuilds, but the build must remain
+# reproducible when a prebuild download times out and compilation is required.
+RUN apk add --no-cache libc6-compat curl bash python3 make g++
RUN corepack enable pnpm
-COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
+COPY .npmrc pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY patches ./patches
+COPY --parents ./apps/*/package.json ./packages/*/package.json ./tooling/*/package.json ./
+# @homarr/definitions generates documentation types during install.
+COPY --parents ./packages/definitions/src ./
# Workaround for pnpm/pnpm#5268: pnpm fetch crashes when patchedDependencies
# are configured with nodeLinker: hoisted. The applyPatchToDir function tries
# to chdir into node_modules/ which doesn't exist during fetch (only the
@@ -22,11 +28,13 @@ RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
pnpm fetch --ignore-scripts && \
sed -i 's/nodeLinker: isolated/nodeLinker: hoisted/' pnpm-workspace.yaml
-COPY . .
-# Follow the pnpm fetch pattern from https://pnpm.io/cli/fetch
-# --frozen-lockfile is omitted as recommended by the pnpm fetch docs
+# Install only from the fetched, committed lockfile so local and Docker builds
+# resolve the same dependency graph. Serial lifecycle builds avoid esbuild's
+# atomic binary replacement racing across the hoisted workspace (pnpm/pnpm#8200).
RUN --mount=type=cache,id=pnpm-store,target=/pnpm/store \
- pnpm install --recursive
+ npm_config_nodedir=/usr/local pnpm install --recursive --offline --frozen-lockfile --child-concurrency=1
+
+COPY . .
ARG SKIP_ENV_VALIDATION='true'
ARG CI='true'
@@ -37,7 +45,9 @@ RUN --mount=type=secret,id=TURBO_API,env=TURBO_API \
--mount=type=secret,id=TURBO_TEAM,env=TURBO_TEAM \
--mount=type=secret,id=TURBO_TOKEN,env=TURBO_TOKEN \
--mount=type=secret,id=TURBO_REMOTE_CACHE_SIGNATURE_KEY,env=TURBO_REMOTE_CACHE_SIGNATURE_KEY \
- TURBO_PLATFORM="${TARGETPLATFORM:-linux/amd64}" pnpm build
+ --mount=type=cache,id=homarr-next-build-${TARGETPLATFORM},target=/app/apps/nextjs/.next/cache,sharing=locked \
+ TURBO_PLATFORM="${TARGETPLATFORM:-linux/amd64}" \
+ pnpm turbo build --filter=@homarr/nextjs... --filter=@homarr/cli
FROM base AS runner
WORKDIR /app
@@ -85,5 +95,6 @@ ENV AUTH_PROVIDERS='credentials'
ENV REDIS_IS_EXTERNAL='false'
ENV NODE_ENV='production'
+EXPOSE 7575
ENTRYPOINT [ "/app/entrypoint.sh" ]
CMD ["sh", "run.sh"]
diff --git a/MANTINE.md b/MANTINE.md
new file mode 100644
index 000000000..a87cbbd48
--- /dev/null
+++ b/MANTINE.md
@@ -0,0 +1,474 @@
+# Mantine UI - LLM Documentation
+
+This index lists Mantine documentation pages formatted for LLMs.
+Each link points to a standalone Markdown file under the /llms path.
+
+For a single consolidated file with all content, use:
+- https://mantine.dev/llms-full.txt
+
+## FAQ
+
+- [Are Mantine components accessible?](https://mantine.dev/llms/q-are-mantine-components-accessible.md): Learn about Mantine components accessibility features
+- [Browser zooms in when input is focused. What should I do?](https://mantine.dev/llms/q-browser-zooms-on-focus.md): Use meta tag to disable browser scaling or increase input size
+- [Can I have color schemes other than light and dark?](https://mantine.dev/llms/q-light-dark-is-not-enough.md): Learn about the difference between color scheme and theme
+- [Can I have different primary color for light and dark color schemes?](https://mantine.dev/llms/q-primary-virtual-color.md): Learn how to use virtual color with primary color in theme object
+- [Can I remove MultiSelect placeholder when the component has selected values?](https://mantine.dev/llms/q-multiselect-value-placeholder.md): Learn why MultiSelect placeholder is not removed when values are selected and how to remove it with CSS
+- [Can I use an array of strings as a list in use-form?](https://mantine.dev/llms/q-list-of-strings-in-use-form.md): Learn about use-form lists limitations
+- [Can I use Mantine components as server components?](https://mantine.dev/llms/q-server-components.md): Learn about use client directive and server components usage
+- [Can I use Mantine with Astro?](https://mantine.dev/llms/q-can-i-use-mantine-with-astro.md): No, Astro does not support React context
+- [Can I use Mantine with Create React App (CRA)?](https://mantine.dev/llms/q-can-i-use-mantine-with-cra.md): Learn how to use Mantine without postcss-preset-mantine or how to eject CRA and customize webpack.config.js and use postcss-preset-mantine
+- [Can I use Mantine with Emotion/styled-components/tailwindcss?](https://mantine.dev/llms/q-third-party-styles.md): Learn about limitations of third-party styles
+- [Can I use Mantine with Vue/Svelte/Angular/etc.?](https://mantine.dev/llms/q-vue-svelte-angular.md): No, Mantine is a React library and does not support other frameworks/libraries
+- [Can I use nested inline styles with Mantine components?](https://mantine.dev/llms/q-nested-inline-styles.md): Nested styles are supported only in CSS files
+- [Can I use PostCSS function in inline styles?](https://mantine.dev/llms/q-postcss-fns-inline.md): Learn where PostCSS functions can be used in Mantine
+- [Can I use private CSS variables to style components?](https://mantine.dev/llms/q-private-css-variables.md): No, it is not safe and will not work with future versions of Mantine.
+- [Can I use SegmentedControl with empty value?](https://mantine.dev/llms/q-segmented-control-no-value.md): SegmentedControl cannot be used without a value
+- [How can I add dynamic CSS styles?](https://mantine.dev/llms/q-dynamic-css-styles.md): Use data attributes, CSS variables or inline styles
+- [How can I add fuzzy search to Select component?](https://mantine.dev/llms/q-select-fuzzy.md): Learn how to integrate third-party fuzzy search libraries with Mantine Select component
+- [How can I add hover styles to an element?](https://mantine.dev/llms/q-how-to-add-hover-styles.md): Learn how to add hover classes to an element with CSS modules, &:hover or @mixin hover
+- [How can I apply styles to all Mantine components?](https://mantine.dev/llms/q-apply-styles-to-all.md): Learn how to use attribute selector to apply styles to all Mantine components
+- [How can I change body background color?](https://mantine.dev/llms/q-body-background.md): Use CSS to change body background color
+- [How can I change component color prop value depending on the color scheme?](https://mantine.dev/llms/q-color-scheme-color.md): Learn how to use CSS variables resolver to change color value depending on the color scheme
+- [How can I change input focus styles?](https://mantine.dev/llms/q-input-focus-styles.md): Learn how to use Styles API with Mantine inputs
+- [How can I change Tabs border color?](https://mantine.dev/llms/q-tabs-border-color.md): Learn how to use Styles API with Tabs component
+- [How can I contribute to the library?](https://mantine.dev/llms/q-how-can-i-contribute.md): There are multiple ways to contribute even without writing code
+- [How can I disable all inputs/input groups inside a form?](https://mantine.dev/llms/q-disable-all-inputs-in-form.md): Learn how to disable all inputs/input groups inside a form with the unstyled Fieldset component
+- [How can I display different elements in light and dark color schemes?](https://mantine.dev/llms/q-light-dark-elements.md): Learn how to hide/show elements based on color scheme
+- [How can I focus the first input with error with use-form?](https://mantine.dev/llms/q-focus-first-input-with-error.md): Learn how to handle focus with use-form hook
+- [How can I get current color scheme value in JavaScript?](https://mantine.dev/llms/q-how-to-get-color-scheme-value-in-js.md): How to use useMantineColorScheme and useComputedColorScheme hooks to get current color scheme value in JavaScript
+- [How can I lint CSS files?](https://mantine.dev/llms/q-how-to-setup-stylelint.md): Learn how to set up Stylelint for Mantine postcss syntax
+- [How can I load fonts in Next.js?](https://mantine.dev/llms/q-next-load-fonts.md): A guide to load custom fonts in Next.js with CSS and next/font package
+- [How can I load fonts in React Router?](https://mantine.dev/llms/q-react-router-load-fonts.md): A guide to load custom fonts in React Router
+- [How can I load fonts in Vite?](https://mantine.dev/llms/q-vite-load-fonts.md): A guide to load custom fonts in Vite
+- [How can I lock scroll in my application?](https://mantine.dev/llms/q-how-to-lock-scroll.md): Use react-remove-scroll library to lock scroll in your application
+- [How can I submit a template to Mantine documentation?](https://mantine.dev/llms/q-submit-template.md): Learn how to create and submit a template to Mantine documentation
+- [How can I test Modal/Drawer/Popover components?](https://mantine.dev/llms/q-portals-testing.md): Learn how to use react-testing-library to test components that use portals and transitions.
+- [How can I test Select/MultiSelect components?](https://mantine.dev/llms/q-combobox-testing.md): Learn how to use react-testing-library to test Select and MultiSelect components.
+- [How can I upload files from Dropzone component?](https://mantine.dev/llms/q-dropzone-upload.md): Learn how to process files dropped into Dropzone component
+- [How is that thing done on the mantine.dev website?](https://mantine.dev/llms/q-how-that-thing-is-done.md): Learn how various elements are implemented on the mantine.dev website
+- [How to align input with a button in a flex container?](https://mantine.dev/llms/q-align-input-button.md): Learn how to align Mantine inputs with buttons using Group component
+- [How to call a function when Modal/Drawer closes and animation completes?](https://mantine.dev/llms/q-how-to-call-function-when-modal-closes.md): How to use transitionProps in Modal/Drawer components
+- [How to change background color for FiveM?](https://mantine.dev/llms/q-fivem-background-color.md): Unset body background color and color-scheme styles for FiveM
+- [How to change input placeholder color?](https://mantine.dev/llms/q-inputs-placeholder-color.md): Learn how to change placeholder color with Styles API
+- [How to integrate custom input with use-form hook?](https://mantine.dev/llms/q-custom-input-use-form.md): Learn how to add use-form support for custom inputs
+- [How to prevent Modal from closing?](https://mantine.dev/llms/q-how-to-prevent-modal-from-closing.md): Learn how to prevent Modal from closing when the user clicks outside of it or presses the Escape key
+- [How to scroll to the top of the form if the form is submitted with errors?](https://mantine.dev/llms/q-scroll-to-the-top-of-the-form.md): Learn how to call a function when the form is submitted with errors
+- [How to update Mantine dependencies?](https://mantine.dev/llms/q-how-to-update-dependencies.md): Learn how to update @mantine/*, @mantinex/* and postcss-preset-mantine dependencies manually or with ncu script
+- [How to use Dropzone with @mantine/form?](https://mantine.dev/llms/q-how-to-use-dropzone-with-form.md): Learn how to use Dropzone with @mantine/form to handle file selection state
+- [How to use Mantine template on GitHub?](https://mantine.dev/llms/q-templates-usage.md): Learn how to create a new repository based on Mantine template
+- [I get hydration warning about data-mantine-color-scheme attribute, what does it mean?](https://mantine.dev/llms/q-color-scheme-hydration-warning.md): Learn how hydration works and how to fix warnings
+- [Is there a comparison with other libraries?](https://mantine.dev/llms/q-other-libs.md): Comparison with other libraries is not provided, you are trusted to make your own decision based on documentation examples
+- [Is there a floating action button component?](https://mantine.dev/llms/q-floating-action-button.md): No, but you can build it with Affix and ActionIcon components
+- [Is there DataGrid component that I can use with Mantine?](https://mantine.dev/llms/q-data-grid-i-need.md): A list of community packages that provide DataGrid component that can be used with Mantine.
+- [It is not possible to pinch to zoom when a Modal is opened. What should I do?](https://mantine.dev/llms/q-pinch-to-zoom-modal.md): Use removeScrollProps to configure react-remove-scroll options
+- [MantineProvider was not found in component tree. What should I do?](https://mantine.dev/llms/q-mantine-provider-missing.md): Learn how to resolve MantineProvider issues
+- [My buttons are transparent and the background is visible only on hover, what is wrong?](https://mantine.dev/llms/q-transparent-buttons.md): You have installed a third-party library that overrides Mantine styles
+- [My Popover dropdown closes when I click on the dropdown of a nested Popover](https://mantine.dev/llms/q-nested-popover-closes.md): Popover dropdown is closed when it detects click-outside events
+- [My styles are broken with disabled JavaScript. What should I do?](https://mantine.dev/llms/q-disabled-js.md): Learn how to use Mantine without JavaScript enabled
+- [My styles are overridden by Mantine components styles, what should I do?](https://mantine.dev/llms/q-styles-order.md): Learn how to use CSS layers to control styles order and prevent Mantine components from overriding your styles
+- [Native browser validation does not work in some components, what should I do?](https://mantine.dev/llms/q-native-required.md): Learn why native browser validation does not work in some components
+- [The use-local-storage hook returns the real value only after mounting. Is it a bug?](https://mantine.dev/llms/q-local-storage-effect.md): Learn how Mantine retrieves local storage value
+- [What is the difference between searchable Select and Autocomplete?](https://mantine.dev/llms/q-select-autocomplete-difference.md): Searchable Select and Autocomplete are similar components, but they serve different purposes.
+- [Where can I find the roadmap?](https://mantine.dev/llms/q-roadmap.md): The roadmap is private and only available to the team.
+- [Why are my Carousel slides in vertical orientation?](https://mantine.dev/llms/q-carousel-missing-styles.md): You forgot to import carousel styles
+- [Why are my notifications displayed at the wrong position?](https://mantine.dev/llms/q-notifications-missing-styles.md): Because you did not import styles
+- [Why can I not use one polymorphic component in the component prop of another polymorphic component?](https://mantine.dev/llms/q-polymorphic-in-polymorphic.md): Learn how polymorphic components types work
+- [Why can I not use value/label data structure with Autocomplete/TagsInput?](https://mantine.dev/llms/q-autocomplete-value-label.md): Learn the difference between Autocomplete and Select components
+- [Why can VSCode not autoimport the Text component?](https://mantine.dev/llms/q-why-vscode-cannot-autoimport-text.md): It is confused by the native Text constructor
+- [Why do I see color scheme flickering on page load?](https://mantine.dev/llms/q-color-scheme-flickering.md): Color scheme flickering is caused by incorrect usage of ColorSchemeScript
+- [Why do my dates components look broken?](https://mantine.dev/llms/q-dates-missing-styles.md): Because you did not import styles
+- [Why does FileButton not work in Menu?](https://mantine.dev/llms/q-file-button-in-menu.md): Learn how to use FileButton in Menu component
+- [Why is it required to have 10 shades per color?](https://mantine.dev/llms/q-ten-shades-per-color.md): Learn how Mantine uses theme colors
+- [Why is my screen completely empty after I](https://mantine.dev/llms/q-notifications-empty-screen.md): You have used Notifications component incorrectly
+
+## Core
+
+- [Accordion](https://mantine.dev/llms/core-accordion.md): Divide content into collapsible sections
+- [ActionIcon](https://mantine.dev/llms/core-action-icon.md): Icon button
+- [Affix](https://mantine.dev/llms/core-affix.md): Renders children inside portal at fixed position
+- [Alert](https://mantine.dev/llms/core-alert.md): Attract user attention with important static message
+- [AlphaSlider](https://mantine.dev/llms/core-alpha-slider.md): Slider component for selecting alpha channel in color pickers (0 – 1)
+- [Anchor](https://mantine.dev/llms/core-anchor.md): Display link with theme styles
+- [AngleSlider](https://mantine.dev/llms/core-angle-slider.md): Pick angle value between 0 and 360
+- [AppShell](https://mantine.dev/llms/core-app-shell.md): Responsive shell for your application with header, navbar, aside and footer
+- [AspectRatio](https://mantine.dev/llms/core-aspect-ratio.md): Maintain responsive consistent width/height ratio
+- [Autocomplete](https://mantine.dev/llms/core-autocomplete.md): Autocomplete user input with any list of options
+- [Avatar](https://mantine.dev/llms/core-avatar.md): Display user profile image, initials or fallback icon
+- [BackgroundImage](https://mantine.dev/llms/core-background-image.md): Displays image as background
+- [Badge](https://mantine.dev/llms/core-badge.md): Display badge, pill or tag
+- [Blockquote](https://mantine.dev/llms/core-blockquote.md): Blockquote with optional cite
+- [Box](https://mantine.dev/llms/core-box.md): Base component for all Mantine components
+- [Breadcrumbs](https://mantine.dev/llms/core-breadcrumbs.md): Separates list of react nodes with given separator
+- [Burger](https://mantine.dev/llms/core-burger.md): Open/close navigation button
+- [Button](https://mantine.dev/llms/core-button.md): Button component to render button or link
+- [Card](https://mantine.dev/llms/core-card.md): Card with sections
+- [Cascader](https://mantine.dev/llms/core-cascader.md): Select a path from hierarchical data with cascading columns
+- [Center](https://mantine.dev/llms/core-center.md): Centers content vertically and horizontally
+- [Checkbox](https://mantine.dev/llms/core-checkbox.md): Capture boolean input from user
+- [Chip](https://mantine.dev/llms/core-chip.md): Pick one or multiple values with inline controls
+- [CloseButton](https://mantine.dev/llms/core-close-button.md): Button with close icon
+- [Code](https://mantine.dev/llms/core-code.md): Inline and block code
+- [Collapse](https://mantine.dev/llms/core-collapse.md): Animate presence with slide down/up transition
+- [ColorInput](https://mantine.dev/llms/core-color-input.md): Capture color from user
+- [ColorPicker](https://mantine.dev/llms/core-color-picker.md): Pick colors in hex(a), rgb(a), hsl(a) and hsv(a) formats
+- [ColorSwatch](https://mantine.dev/llms/core-color-swatch.md): Displays color
+- [Combobox](https://mantine.dev/llms/core-combobox.md): Create custom select, autocomplete or multiselect inputs
+- [ComboboxPopover](https://mantine.dev/llms/core-combobox-popover.md): Add combobox dropdown to any button element
+- [Container](https://mantine.dev/llms/core-container.md): Center content with padding and max-width
+- [CopyButton](https://mantine.dev/llms/core-copy-button.md): Copies given text to clipboard
+- [CorePackage](https://mantine.dev/llms/core-package.md)
+- [DataList](https://mantine.dev/llms/core-data-list.md): Display label-value pairs as a description list
+- [Dialog](https://mantine.dev/llms/core-dialog.md): Display a fixed overlay dialog at any side of the screen
+- [Divider](https://mantine.dev/llms/core-divider.md): Horizontal line with optional label or vertical divider
+- [Drawer](https://mantine.dev/llms/core-drawer.md): Display overlay area at any side of the screen
+- [EmptyState](https://mantine.dev/llms/core-empty-state.md): Display a placeholder for no data, empty results and first-run states
+- [Fieldset](https://mantine.dev/llms/core-fieldset.md): Group related elements in a form
+- [FileButton](https://mantine.dev/llms/core-file-button.md): Open file picker with a button click
+- [FileInput](https://mantine.dev/llms/core-file-input.md): Capture files from user
+- [Flex](https://mantine.dev/llms/core-flex.md): Compose elements in a flex container
+- [FloatingIndicator](https://mantine.dev/llms/core-floating-indicator.md): Display a floating indicator over a group of elements
+- [FloatingWindow](https://mantine.dev/llms/core-floating-window.md): Draggable floating area
+- [FocusTrap](https://mantine.dev/llms/core-focus-trap.md): Trap focus at child node
+- [Grid](https://mantine.dev/llms/core-grid.md): Responsive 12 columns grid system
+- [Group](https://mantine.dev/llms/core-group.md): Compose elements and components in a horizontal flex container
+- [Highlight](https://mantine.dev/llms/core-highlight.md): Highlight given part of a string with mark
+- [HoverCard](https://mantine.dev/llms/core-hover-card.md): Display popover section when target element is hovered
+- [HueSlider](https://mantine.dev/llms/core-hue-slider.md): Slider component for selecting hue channel in color pickers (0 – 360 degrees)
+- [Image](https://mantine.dev/llms/core-image.md): Image with optional fallback
+- [Indicator](https://mantine.dev/llms/core-indicator.md): Display element at the corner of another element
+- [Input](https://mantine.dev/llms/core-input.md): Base component to create custom inputs
+- [JsonInput](https://mantine.dev/llms/core-json-input.md): Capture json data from user
+- [Kbd](https://mantine.dev/llms/core-kbd.md): Display keyboard key
+- [List](https://mantine.dev/llms/core-list.md): Display ordered or unordered list
+- [Loader](https://mantine.dev/llms/core-loader.md): Indicate loading state
+- [LoadingOverlay](https://mantine.dev/llms/core-loading-overlay.md): An overlay with centered loader
+- [Mark](https://mantine.dev/llms/core-mark.md): Highlight part of the text
+- [Marquee](https://mantine.dev/llms/core-marquee.md): Create continuous scrolling animation for content
+- [MaskInput](https://mantine.dev/llms/core-mask-input.md): Input with mask pattern for formatted text entry
+- [Menu](https://mantine.dev/llms/core-menu.md): Combine a list of secondary actions into single interactive area
+- [Menubar](https://mantine.dev/llms/core-menubar.md): Desktop application style menubar with a row of menu triggers
+- [Modal](https://mantine.dev/llms/core-modal.md): An accessible overlay dialog
+- [MultiSelect](https://mantine.dev/llms/core-multi-select.md): Custom searchable multi select
+- [NativeSelect](https://mantine.dev/llms/core-native-select.md): Native select element based on Input
+- [NavLink](https://mantine.dev/llms/core-nav-link.md): Navigation link
+- [Notification](https://mantine.dev/llms/core-notification.md): Show dynamic notifications and alerts to user, part of notifications system
+- [NumberFormatter](https://mantine.dev/llms/core-number-formatter.md): Format number with thousands/decimal separators and suffix/prefix
+- [NumberInput](https://mantine.dev/llms/core-number-input.md): Capture number from user
+- [OverflowList](https://mantine.dev/llms/core-overflow-list.md): Hide items that do not fit in a container and display them as a single collapsed item
+- [Overlay](https://mantine.dev/llms/core-overlay.md): Overlays parent element with div element with any color and opacity
+- [Pagination](https://mantine.dev/llms/core-pagination.md): Display active page and navigate between multiple pages
+- [Paper](https://mantine.dev/llms/core-paper.md): Renders white or dark background depending on color scheme
+- [PasswordInput](https://mantine.dev/llms/core-password-input.md): Capture password data from user
+- [Pill](https://mantine.dev/llms/core-pill.md): Removable and non-removable tags
+- [PillsInput](https://mantine.dev/llms/core-pills-input.md): Base component for custom tags inputs and multi selects
+- [PinInput](https://mantine.dev/llms/core-pin-input.md): Capture pin code or one time password from the user
+- [Popover](https://mantine.dev/llms/core-popover.md): Display popover section relative to given target element
+- [Portal](https://mantine.dev/llms/core-portal.md): Renders component outside of parent element tree
+- [Progress](https://mantine.dev/llms/core-progress.md): Give user feedback for status of the task
+- [Radio](https://mantine.dev/llms/core-radio.md): Wrapper for input type radio
+- [RangeSlider](https://mantine.dev/llms/core-range-slider.md): RangeSlider component
+- [Rating](https://mantine.dev/llms/core-rating.md): Pick and display rating
+- [RingProgress](https://mantine.dev/llms/core-ring-progress.md): Give user feedback for status of the task with circle diagram
+- [RollingNumber](https://mantine.dev/llms/core-rolling-number.md): Animate value changes with rolling digits
+- [ScrollArea](https://mantine.dev/llms/core-scroll-area.md): Area with custom scrollbars
+- [Scroller](https://mantine.dev/llms/core-scroller.md): Horizontal scroll container with navigation controls
+- [SegmentedControl](https://mantine.dev/llms/core-segmented-control.md): A linear set of two or more segments
+- [Select](https://mantine.dev/llms/core-select.md): Custom searchable select
+- [SemiCircleProgress](https://mantine.dev/llms/core-semi-circle-progress.md): Represent progress with semi circle diagram
+- [SimpleGrid](https://mantine.dev/llms/core-simple-grid.md): Responsive grid in which each item takes equal amount of space
+- [Skeleton](https://mantine.dev/llms/core-skeleton.md): Indicate content loading state
+- [Slider](https://mantine.dev/llms/core-slider.md): Slider component
+- [Space](https://mantine.dev/llms/core-space.md): Add horizontal or vertical spacing from theme
+- [Splitter](https://mantine.dev/llms/core-splitter.md): Resizable split pane layout
+- [Spoiler](https://mantine.dev/llms/core-spoiler.md): Hide long sections of content under a spoiler
+- [Stack](https://mantine.dev/llms/core-stack.md): Compose elements and components in a vertical flex container
+- [Stepper](https://mantine.dev/llms/core-stepper.md): Display content divided into a steps sequence
+- [Switch](https://mantine.dev/llms/core-switch.md): Capture boolean input from user
+- [Table](https://mantine.dev/llms/core-table.md): Render table with theme styles
+- [TableOfContents](https://mantine.dev/llms/core-table-of-contents.md): Renders a list of headings on the page and tracks current heading visible in the viewport
+- [Tabs](https://mantine.dev/llms/core-tabs.md): Switch between different views
+- [TagsInput](https://mantine.dev/llms/core-tags-input.md): Capture a list of values from user with free input and suggestions
+- [Text](https://mantine.dev/llms/core-text.md): Display text
+- [Textarea](https://mantine.dev/llms/core-textarea.md): Autosize or regular textarea
+- [TextInput](https://mantine.dev/llms/core-text-input.md): Capture string input from user
+- [ThemeIcon](https://mantine.dev/llms/core-theme-icon.md): Render icon inside element with theme colors
+- [Timeline](https://mantine.dev/llms/core-timeline.md): Display list of events in chronological order
+- [Title](https://mantine.dev/llms/core-title.md): h1-h6 heading
+- [Tooltip](https://mantine.dev/llms/core-tooltip.md): Renders tooltip at given element on mouse over or other event
+- [Transition](https://mantine.dev/llms/core-transition.md): Animate presence of component with pre-made animations
+- [Tree](https://mantine.dev/llms/core-tree.md): Display a Tree structure
+- [TreeSelect](https://mantine.dev/llms/core-tree-select.md): Select with hierarchical tree data
+- [Typography](https://mantine.dev/llms/core-typography.md): Styles provider for html content
+- [UnstyledButton](https://mantine.dev/llms/core-unstyled-button.md): Unstyled polymorphic button
+- [VisuallyHidden](https://mantine.dev/llms/core-visually-hidden.md): Hide element visually but keep it accessible for screen readers
+
+## Hooks
+
+- [HooksPackage](https://mantine.dev/llms/hooks-package.md)
+- [use-element-size](https://mantine.dev/llms/hooks-use-element-size.md): Returns element width and height and observes changes with ResizeObserver
+- [useClickOutside](https://mantine.dev/llms/hooks-use-click-outside.md)
+- [useClipboard](https://mantine.dev/llms/hooks-use-clipboard.md)
+- [useCollapse](https://mantine.dev/llms/hooks-use-collapse.md)
+- [useColorScheme](https://mantine.dev/llms/hooks-use-color-scheme.md)
+- [useCounter](https://mantine.dev/llms/hooks-use-counter.md)
+- [useDebouncedCallback](https://mantine.dev/llms/hooks-use-debounced-callback.md)
+- [useDebouncedState](https://mantine.dev/llms/hooks-use-debounced-state.md)
+- [useDebouncedValue](https://mantine.dev/llms/hooks-use-debounced-value.md)
+- [useDidUpdate](https://mantine.dev/llms/hooks-use-did-update.md)
+- [useDisclosure](https://mantine.dev/llms/hooks-use-disclosure.md)
+- [useDocumentTitle](https://mantine.dev/llms/hooks-use-document-title.md)
+- [useDocumentVisibility](https://mantine.dev/llms/hooks-use-document-visibility.md)
+- [useDrag](https://mantine.dev/llms/hooks-use-drag.md)
+- [useEventListener](https://mantine.dev/llms/hooks-use-event-listener.md)
+- [useEyeDropper](https://mantine.dev/llms/hooks-use-eye-dropper.md)
+- [useFavicon](https://mantine.dev/llms/hooks-use-favicon.md)
+- [useFetch](https://mantine.dev/llms/hooks-use-fetch.md)
+- [useFileDialog](https://mantine.dev/llms/hooks-use-file-dialog.md)
+- [useFloatingWindow](https://mantine.dev/llms/hooks-use-floating-window.md)
+- [useFocusReturn](https://mantine.dev/llms/hooks-use-focus-return.md)
+- [useFocusTrap](https://mantine.dev/llms/hooks-use-focus-trap.md)
+- [useFocusWithin](https://mantine.dev/llms/hooks-use-focus-within.md)
+- [useForceUpdate](https://mantine.dev/llms/hooks-use-force-update.md)
+- [useFullscreen](https://mantine.dev/llms/hooks-use-fullscreen.md)
+- [useHash](https://mantine.dev/llms/hooks-use-hash.md)
+- [useHeadroom](https://mantine.dev/llms/hooks-use-headroom.md)
+- [useHotkeys](https://mantine.dev/llms/hooks-use-hotkeys.md)
+- [useHover](https://mantine.dev/llms/hooks-use-hover.md)
+- [useId](https://mantine.dev/llms/hooks-use-id.md)
+- [useIdle](https://mantine.dev/llms/hooks-use-idle.md)
+- [useInputState](https://mantine.dev/llms/hooks-use-input-state.md)
+- [useIntersection](https://mantine.dev/llms/hooks-use-intersection.md)
+- [useInterval](https://mantine.dev/llms/hooks-use-interval.md)
+- [useInViewport](https://mantine.dev/llms/hooks-use-in-viewport.md)
+- [useIsFirstRender](https://mantine.dev/llms/hooks-use-is-first-render.md)
+- [useIsomorphicEffect](https://mantine.dev/llms/hooks-use-isomorphic-effect.md)
+- [useListState](https://mantine.dev/llms/hooks-use-list-state.md)
+- [useLocalStorage](https://mantine.dev/llms/hooks-use-local-storage.md)
+- [useLogger](https://mantine.dev/llms/hooks-use-logger.md)
+- [useLongPress](https://mantine.dev/llms/hooks-use-long-press.md)
+- [useMap](https://mantine.dev/llms/hooks-use-map.md)
+- [useMask](https://mantine.dev/llms/hooks-use-mask.md)
+- [useMediaQuery](https://mantine.dev/llms/hooks-use-media-query.md)
+- [useMergedRef](https://mantine.dev/llms/hooks-use-merged-ref.md)
+- [useMounted](https://mantine.dev/llms/hooks-use-mounted.md)
+- [useMouse](https://mantine.dev/llms/hooks-use-mouse.md)
+- [useMove](https://mantine.dev/llms/hooks-use-move.md)
+- [useMutationObserver](https://mantine.dev/llms/hooks-use-mutation-observer.md)
+- [useNetwork](https://mantine.dev/llms/hooks-use-network.md)
+- [useOrientation](https://mantine.dev/llms/hooks-use-orientation.md)
+- [useOs](https://mantine.dev/llms/hooks-use-os.md)
+- [usePageLeave](https://mantine.dev/llms/hooks-use-page-leave.md)
+- [usePagination](https://mantine.dev/llms/hooks-use-pagination.md)
+- [usePrevious](https://mantine.dev/llms/hooks-use-previous.md)
+- [useQueue](https://mantine.dev/llms/hooks-use-queue.md)
+- [useRadialMove](https://mantine.dev/llms/hooks-use-radial-move.md)
+- [useReducedMotion](https://mantine.dev/llms/hooks-use-reduced-motion.md)
+- [useResizeObserver](https://mantine.dev/llms/hooks-use-resize-observer.md)
+- [useRovingIndex](https://mantine.dev/llms/hooks-use-roving-index.md)
+- [useScrollDirection](https://mantine.dev/llms/hooks-use-scroll-direction.md)
+- [useScroller](https://mantine.dev/llms/hooks-use-scroller.md)
+- [useScrollIntoView](https://mantine.dev/llms/hooks-use-scroll-into-view.md)
+- [useScrollSpy](https://mantine.dev/llms/hooks-use-scroll-spy.md)
+- [useSelection](https://mantine.dev/llms/hooks-use-selection.md)
+- [useSet](https://mantine.dev/llms/hooks-use-set.md)
+- [useSetState](https://mantine.dev/llms/hooks-use-set-state.md)
+- [useShallowEffect](https://mantine.dev/llms/hooks-use-shallow-effect.md)
+- [useSplitter](https://mantine.dev/llms/hooks-use-splitter.md)
+- [useStateHistory](https://mantine.dev/llms/hooks-use-state-history.md)
+- [useTextSelection](https://mantine.dev/llms/hooks-use-text-selection.md)
+- [useThrottledCallback](https://mantine.dev/llms/hooks-use-throttled-callback.md)
+- [useThrottledState](https://mantine.dev/llms/hooks-use-throttled-state.md)
+- [useThrottledValue](https://mantine.dev/llms/hooks-use-throttled-value.md)
+- [useTimeout](https://mantine.dev/llms/hooks-use-timeout.md)
+- [useToggle](https://mantine.dev/llms/hooks-use-toggle.md)
+- [useUncontrolled](https://mantine.dev/llms/hooks-use-uncontrolled.md)
+- [useValidatedState](https://mantine.dev/llms/hooks-use-validated-state.md)
+- [useViewportSize](https://mantine.dev/llms/hooks-use-viewport-size.md)
+- [useWindowEvent](https://mantine.dev/llms/hooks-use-window-event.md)
+- [useWindowScroll](https://mantine.dev/llms/hooks-use-window-scroll.md)
+
+## Form
+
+- [Form context](https://mantine.dev/llms/form-create-form-context.md): Add context support to use-form with createFormContext
+- [formActions](https://mantine.dev/llms/form-actions.md)
+- [formAllInputs](https://mantine.dev/llms/form-all-inputs.md)
+- [formErrors](https://mantine.dev/llms/form-errors.md)
+- [formGetInputProps](https://mantine.dev/llms/form-get-input-props.md)
+- [formNested](https://mantine.dev/llms/form-nested.md)
+- [FormPackage](https://mantine.dev/llms/form-package.md)
+- [formRecipes](https://mantine.dev/llms/form-recipes.md)
+- [formSchemaValidation](https://mantine.dev/llms/form-schema-validation.md)
+- [formStatus](https://mantine.dev/llms/form-status.md)
+- [formUncontrolled](https://mantine.dev/llms/form-uncontrolled.md)
+- [formValidation](https://mantine.dev/llms/form-validation.md)
+- [formValidators](https://mantine.dev/llms/form-validators.md)
+- [formValues](https://mantine.dev/llms/form-values.md)
+- [use-field](https://mantine.dev/llms/form-use-field.md): use-field hook – manage single field state
+- [use-form](https://mantine.dev/llms/form-use-form.md): Manage form state
+
+## Dates
+
+- [Calendar](https://mantine.dev/llms/dates-calendar.md): Base component for custom date pickers
+- [DateInput](https://mantine.dev/llms/dates-date-input.md): Free form date input
+- [DatePicker](https://mantine.dev/llms/dates-date-picker.md): Inline date, multiple dates and dates range picker
+- [DatePickerInput](https://mantine.dev/llms/dates-date-picker-input.md): Date, multiple dates and dates range picker input
+- [DateTimePicker](https://mantine.dev/llms/dates-date-time-picker.md): Capture datetime from the user
+- [GettingStartedDates](https://mantine.dev/llms/dates-getting-started.md)
+- [InlineDateTimePicker](https://mantine.dev/llms/dates-inline-date-time-picker.md): Inline date and time picker with range support
+- [MiniCalendar](https://mantine.dev/llms/dates-mini-calendar.md): Compact calendar to display a small number of days in a row
+- [MonthPicker](https://mantine.dev/llms/dates-month-picker.md): Inline month, multiple months and months range picker
+- [MonthPickerInput](https://mantine.dev/llms/dates-month-picker-input.md): Month, multiple months and months range picker input
+- [TimeGrid](https://mantine.dev/llms/dates-time-grid.md): Captures time value from the user with a predefined set of options
+- [TimeInput](https://mantine.dev/llms/dates-time-input.md): Capture time from the user
+- [TimePicker](https://mantine.dev/llms/dates-time-picker.md): Captures time value from the user
+- [TimeValue](https://mantine.dev/llms/dates-time-value.md): Display a formatted time value
+- [YearPicker](https://mantine.dev/llms/dates-year-picker.md): Inline year, multiple years and years range picker
+- [YearPickerInput](https://mantine.dev/llms/dates-year-picker-input.md): Inline year, multiple years and years range picker
+
+## Charts
+
+- [AreaChart](https://mantine.dev/llms/charts-area-chart.md): Area chart component with stacked, percent and split variants
+- [BarChart](https://mantine.dev/llms/charts-bar-chart.md): Bar chart component with stacked and percent variants
+- [BarsList](https://mantine.dev/llms/charts-bars-list.md): Display a list of bars with names and values
+- [BubbleChart](https://mantine.dev/llms/charts-bubble-chart.md): Bubble chart component
+- [BulletChart](https://mantine.dev/llms/charts-bullet-chart.md): Compact KPI chart with value, target, and qualitative ranges
+- [CompositeChart](https://mantine.dev/llms/charts-composite-chart.md): Composed chart with support for Area, Bar and Line charts
+- [DonutChart](https://mantine.dev/llms/charts-donut-chart.md): Donut chart component
+- [FunnelChart](https://mantine.dev/llms/charts-funnel-chart.md): Funnel chart component
+- [GettingStartedCharts](https://mantine.dev/llms/charts-getting-started.md)
+- [Heatmap](https://mantine.dev/llms/charts-heatmap.md): Heatmap chart component
+- [LineChart](https://mantine.dev/llms/charts-line-chart.md): Line chart component
+- [PieChart](https://mantine.dev/llms/charts-pie-chart.md): Pie chart component
+- [RadarChart](https://mantine.dev/llms/charts-radar-chart.md): Radar chart component
+- [RadialBarChart](https://mantine.dev/llms/charts-radial-bar-chart.md): Radial bar chart component
+- [SankeyChart](https://mantine.dev/llms/charts-sankey-chart.md): Sankey diagram component
+- [ScatterChart](https://mantine.dev/llms/charts-scatter-chart.md): Scatter chart component
+- [Sparkline](https://mantine.dev/llms/charts-sparkline.md): Simplified area chart to show trends
+- [SunburstChart](https://mantine.dev/llms/charts-sunburst-chart.md): Sunburst chart component for hierarchical data
+- [Treemap](https://mantine.dev/llms/charts-treemap.md): Treemap chart component
+
+## Schedule
+
+- [AgendaView](https://mantine.dev/llms/schedule-agenda-view.md): Vertical list view of events grouped by date
+- [DayView](https://mantine.dev/llms/schedule-day-view.md): Standalone schedule day view component
+- [GettingStartedSchedule](https://mantine.dev/llms/schedule-getting-started.md)
+- [MobileMonthView](https://mantine.dev/llms/schedule-mobile-month-view.md): Mobile-optimized schedule month view component
+- [MonthView](https://mantine.dev/llms/schedule-month-view.md): Standalone schedule month view component
+- [RecurringEventsSchedule](https://mantine.dev/llms/schedule-recurring-events.md)
+- [ResourcesDayView](https://mantine.dev/llms/schedule-resources-day-view.md): Day view with resource columns for scheduling across resources
+- [ResourcesMonthView](https://mantine.dev/llms/schedule-resources-month-view.md): Month view with resource lanes for scheduling across resources
+- [ResourcesSchedule](https://mantine.dev/llms/schedule-resources-schedule.md): Schedule wrapper component that combines resource day, week and month views
+- [ResourcesWeekView](https://mantine.dev/llms/schedule-resources-week-view.md): Week view with resource rows for scheduling across resources
+- [Schedule](https://mantine.dev/llms/schedule-schedule.md): Schedule component with day, week, month and year views
+- [ScheduleEventsData](https://mantine.dev/llms/schedule-events-data.md)
+- [WeekView](https://mantine.dev/llms/schedule-week-view.md): Standalone schedule week view component
+- [YearView](https://mantine.dev/llms/schedule-year-view.md): Standalone schedule year view component
+
+## Guides
+
+- [ControlledVsUncontrolled](https://mantine.dev/llms/guides-controlled-vs-uncontrolled.md)
+- [CustomComponents](https://mantine.dev/llms/guides-custom-components.md)
+- [EightToNine](https://mantine.dev/llms/guides-8x-to-9x.md)
+- [FunctionsReference](https://mantine.dev/llms/guides-functions-reference.md)
+- [Gatsby](https://mantine.dev/llms/guides-gatsby.md)
+- [Icons](https://mantine.dev/llms/guides-icons.md)
+- [JavaScript](https://mantine.dev/llms/guides-javascript.md)
+- [Jest](https://mantine.dev/llms/guides-jest.md)
+- [LLMDocumentation](https://mantine.dev/llms/guides-llms.md)
+- [NextJs](https://mantine.dev/llms/guides-next.md)
+- [Polymorphic](https://mantine.dev/llms/guides-polymorphic.md)
+- [ReactRouter](https://mantine.dev/llms/guides-react-router.md)
+- [Redwood](https://mantine.dev/llms/guides-redwood.md)
+- [SevenToEight](https://mantine.dev/llms/guides-7x-to-8x.md)
+- [SixToSeven](https://mantine.dev/llms/guides-6x-to-7x.md)
+- [Storybook](https://mantine.dev/llms/guides-storybook.md)
+- [Tiptap3Migration](https://mantine.dev/llms/guides-tiptap-3-migration.md)
+- [TypeScript](https://mantine.dev/llms/guides-typescript.md)
+- [Vite](https://mantine.dev/llms/guides-vite.md)
+- [Vitest](https://mantine.dev/llms/guides-vitest.md)
+
+## Theming
+
+- [Colors](https://mantine.dev/llms/theming-colors.md)
+- [ColorSchemes](https://mantine.dev/llms/theming-color-schemes.md)
+- [DefaultProps](https://mantine.dev/llms/theming-default-props.md)
+- [MantineProvider](https://mantine.dev/llms/theming-mantine-provider.md)
+- [ThemeObject](https://mantine.dev/llms/theming-theme-object.md)
+- [Typography](https://mantine.dev/llms/theming-typography.md): Styles provider for html content
+
+## Styles
+
+- [ColorFunctions](https://mantine.dev/llms/styles-color-functions.md)
+- [CSSFilesList](https://mantine.dev/llms/styles-css-files-list.md)
+- [CSSModules](https://mantine.dev/llms/styles-css-modules.md)
+- [CssVariables](https://mantine.dev/llms/styles-css-variables.md)
+- [CSSVariablesList](https://mantine.dev/llms/styles-css-variables-list.md)
+- [DataAttributes](https://mantine.dev/llms/styles-data-attributes.md)
+- [Emotion](https://mantine.dev/llms/styles-emotion.md)
+- [GlobalStyles](https://mantine.dev/llms/styles-global-styles.md)
+- [MantineStyles](https://mantine.dev/llms/styles-mantine-styles.md)
+- [PostCSSPreset](https://mantine.dev/llms/styles-postcss-preset.md)
+- [Rem](https://mantine.dev/llms/styles-rem.md)
+- [ResponsiveStyles](https://mantine.dev/llms/styles-responsive.md)
+- [Rtl](https://mantine.dev/llms/styles-rtl.md)
+- [StyleProp](https://mantine.dev/llms/styles-style.md)
+- [StyleProps](https://mantine.dev/llms/styles-style-props.md)
+- [StylesApi](https://mantine.dev/llms/styles-styles-api.md)
+- [StylesOverview](https://mantine.dev/llms/styles-styles-overview.md)
+- [StylesPerformance](https://mantine.dev/llms/styles-styles-performance.md)
+- [UnstyledComponents](https://mantine.dev/llms/styles-unstyled.md)
+- [UsageWithSass](https://mantine.dev/llms/styles-sass.md)
+- [VanillaExtract](https://mantine.dev/llms/styles-vanilla-extract.md)
+- [VariantsAndSizes](https://mantine.dev/llms/styles-variants-sizes.md)
+
+## X
+
+- [Carousel](https://mantine.dev/llms/x-carousel.md): Embla based carousel component
+- [CodeHighlight](https://mantine.dev/llms/x-code-highlight.md): Highlight code with shiki or highlight.js
+- [Dropzone](https://mantine.dev/llms/x-dropzone.md): Capture files from user with drag and drop
+- [ExtensionsPackage](https://mantine.dev/llms/x-extensions.md)
+- [Modals manager](https://mantine.dev/llms/x-modals.md): Centralized modals manager with option to handle state of multi-step modals
+- [NavigationProgress](https://mantine.dev/llms/x-nprogress.md): Navigation progress bar
+- [Notifications system](https://mantine.dev/llms/x-notifications.md): Mantine notifications system
+- [Rich text editor](https://mantine.dev/llms/x-tiptap.md): Tiptap based rich text editor
+- [Spotlight](https://mantine.dev/llms/x-spotlight.md): Command center for your application
+
+## Other
+
+- [About](https://mantine.dev/llms/about.md)
+- [AllReleases](https://mantine.dev/llms/changelog-all-releases.md)
+- [BrowserSupport](https://mantine.dev/llms/browser-support.md)
+- [Changelog700](https://mantine.dev/llms/changelog-7-0-0.md)
+- [Changelog710](https://mantine.dev/llms/changelog-7-1-0.md)
+- [Changelog7100](https://mantine.dev/llms/changelog-7-10-0.md)
+- [Changelog7110](https://mantine.dev/llms/changelog-7-11-0.md)
+- [Changelog7120](https://mantine.dev/llms/changelog-7-12-0.md)
+- [Changelog7130](https://mantine.dev/llms/changelog-7-13-0.md)
+- [Changelog7140](https://mantine.dev/llms/changelog-7-14-0.md)
+- [Changelog7150](https://mantine.dev/llms/changelog-7-15-0.md)
+- [Changelog7160](https://mantine.dev/llms/changelog-7-16-0.md)
+- [Changelog7170](https://mantine.dev/llms/changelog-7-17-0.md)
+- [Changelog720](https://mantine.dev/llms/changelog-7-2-0.md)
+- [Changelog730](https://mantine.dev/llms/changelog-7-3-0.md)
+- [Changelog740](https://mantine.dev/llms/changelog-7-4-0.md)
+- [Changelog750](https://mantine.dev/llms/changelog-7-5-0.md)
+- [Changelog760](https://mantine.dev/llms/changelog-7-6-0.md)
+- [Changelog770](https://mantine.dev/llms/changelog-7-7-0.md)
+- [Changelog780](https://mantine.dev/llms/changelog-7-8-0.md)
+- [Changelog790](https://mantine.dev/llms/changelog-7-9-0.md)
+- [Changelog800](https://mantine.dev/llms/changelog-8-0-0.md)
+- [Changelog810](https://mantine.dev/llms/changelog-8-1-0.md)
+- [Changelog820](https://mantine.dev/llms/changelog-8-2-0.md)
+- [Changelog830](https://mantine.dev/llms/changelog-8-3-0.md)
+- [Changelog900](https://mantine.dev/llms/changelog-9-0-0.md)
+- [Changelog910](https://mantine.dev/llms/changelog-9-1-0.md)
+- [Changelog920](https://mantine.dev/llms/changelog-9-2-0.md)
+- [Changelog930](https://mantine.dev/llms/changelog-9-3-0.md)
+- [Changelog940](https://mantine.dev/llms/changelog-9-4-0.md)
+- [Changelog950](https://mantine.dev/llms/changelog-9-5-0.md)
+- [Contribute](https://mantine.dev/llms/contribute.md)
+- [GettingStarted](https://mantine.dev/llms/getting-started.md)
+- [NotFound](https://mantine.dev/llms/404.md)
+- [OxcConfig](https://mantine.dev/llms/oxc-config-mantine.md)
+- [PreviousChangelogs](https://mantine.dev/llms/changelog-previous-versions.md)
+- [Support](https://mantine.dev/llms/support.md)
diff --git a/PRODUCT.md b/PRODUCT.md
new file mode 100644
index 000000000..bac6f41bd
--- /dev/null
+++ b/PRODUCT.md
@@ -0,0 +1,39 @@
+# Product
+
+## Register
+
+product
+
+## Users
+
+Homarr is for self-hosters and administrators who assemble dashboards for services across home labs and private infrastructure. They need dense, reliable controls and status at a glance, while retaining enough guidance to configure advanced features without hiding how they work.
+
+## Product Purpose
+
+Homarr provides one place to organize, observe, and operate self-hosted services. Success means users can build useful dashboards quickly, understand their state, recover from configuration errors, act on services, and reshape their workspace without repeatedly leaving the board. Boards must load immediately, stay visually stable, and keep widgets legible across screen sizes.
+
+## Brand Personality
+
+Direct, capable, precise, and approachable. Homarr should feel calm under load, be truthful about system state, and reward users who refine details.
+
+## Anti-references
+
+- Decorative dashboards, oversized metric cards, and motion without state meaning.
+- Wizard flows that fight ordinary scrolling or rigid layouts that force one workflow.
+- Hidden configuration magic and implementation complexity exposed before it is needed.
+- Clever custom controls where familiar platform or Mantine interactions already work.
+
+## Design Principles
+
+1. Keep configuration, consequences, and system state visible.
+2. Make simple tasks short without limiting advanced workflows.
+3. Preserve expert depth while prioritizing urgent information at small sizes.
+4. Reveal dense workspaces progressively through a consistent advanced focus mode.
+5. Isolate failures and show loading, stale, partial, empty, and failed states truthfully.
+6. Use the existing Mantine design language consistently across management and board surfaces.
+7. Keep spatial actions direct: moving, resizing, nesting, and using fixed rails happen on the canvas with immediate feedback.
+8. Preserve predictable widget composition with fixed logical tile sizes and horizontal scrolling before controls become inaccessible.
+
+## Accessibility & Inclusion
+
+Target WCAG 2.2 AA. Every pointer interaction needs a keyboard and coarse-pointer path. Support screen readers, visible and restored focus, sufficient contrast, reduced motion, non-color status cues, responsive layouts, actionable error messages, and keyboard equivalents for drag and resize operations.
diff --git a/REVIEW.md b/REVIEW.md
new file mode 100644
index 000000000..d78b4fc5e
--- /dev/null
+++ b/REVIEW.md
@@ -0,0 +1,263 @@
+# PR #6502 widget-modernization ultra-review
+
+## Executive result
+
+**Merge recommendation: changes required.** No P0 was found, but the current branch still has five P1 issues: a pre-existing Custom API authorization/redirect weakness exposed by this subsystem review, an inaccessible manual advanced-focus interaction, a likely fixed-position/transform geometry defect, systemic false empty states for failed queries, and unvalidated integration-provided navigation URLs. Eight P2 issues cover raw error disclosure, unsafe runtime-ref interfaces, localization, responsive/theming drift, test coverage, and inaccurate PR copy.
+
+Audit health (Impeccable rubric): accessibility 1/4, performance 2/4, responsive design 2/4, theming 2/4, anti-patterns 3/4; **10/20 (Acceptable, significant work required)**. The UI does not look generically AI-generated; its problem is inconsistent technical behavior beneath an otherwise coherent Mantine vocabulary.
+
+## Reviewed revision and environment
+
+| Item | Value |
+| --- | --- |
+| PR | #6502, `feat: modernize dashboard widgets` |
+| Reviewed head | `96e92485a5fd45f3648cfac0453e614779c414ba` |
+| Base branch / fetched base | `dev` / `d58a47b776a55d04dee2220f59d072b9af8bbed3` |
+| Merge base | `d58a47b776a55d04dee2220f59d072b9af8bbed3` |
+| Diff | 359 files, +15,721/-4,478 |
+| PR state | open, non-draft, `BLOCKED`; no approval decision |
+| Hosted checks at audit start | Fast gate, Container and E2E, CodeQL, JS/TS analysis, Actions analysis, title, auto-fix: green |
+| Inline threads | 34 total, 7 unresolved; only 2 remain substantively valid |
+| Mantine | repository lock: `@mantine/core`, hooks, charts and dates 9.4.1 |
+| Local tools | Node 24.16.0 and pnpm 9.5.0; repository requires Node >=24.18.0 and pnpm >=11.15.1 |
+
+The former red baseline `7b7045202d289b7d83946befcee5d2845ea4a706` had a green Fast gate and a failed Container/E2E run (`30748255760`, job `91498308523`). The latest head has a green Container/E2E job (`30756067198`, job `91519039504`). This proves the current suite passed once; it does not satisfy the requested three-run flake characterization.
+
+## PR-description traceability
+
+| Claim | Result | Evidence / correction |
+| --- | --- | --- |
+| “all 54 widget renderers with compact and advanced modes” | **Inaccurate** | 54 `WidgetKind` values exist. `displayMode="advanced"` reaches 53; `app` is deliberately excluded in `item-content.tsx:51` and documented as compact-only. Change copy to “53 widget renderers; app tiles remain compact,” or implement app advanced mode. |
+| advanced focus: hover, keyboard, touch, geometry | **Partial** | Entry paths, Escape ownership, reduced motion, close focus, touch button and zero-dimension guards exist. WID-002/003 remain. |
+| richer widget context actions | **Partial** | `Menu.ContextMenu` is a good Mantine choice and query scope is item-aware. Dispatch still crosses `any`; Custom API action authorization is not represented. |
+| standardized query identity/cache/status | **Partial** | Query keys, matchers, runtime capture, recovery and tests exist. Thirty-nine of 53 renderer query calls do not read errors; runtime ref registration is render-time mutable state. |
+| safe polling | **Mostly confirmed** | defaults are limited to `widget` namespace; Custom API resume immediately refetches; per-widget intervals exist. State/ref typing still needs redesign. |
+| partial-failure rendering | **Partial** | settlement helper and several routers are strong; many renderer-level total failures still appear empty/disconnected. |
+| bounded integration responses | **Mostly confirmed** | common bounded reader, Custom API 2 MiB, timetable 512 KiB, Traefik bounds and Beszel queue bounds exist. Custom action execution remains outside the hardened fetch path. |
+| timetable hardening | **Confirmed** | board/item permission checks, saved-value comparison, public-address resolution, private ranges, per-request DNS re-resolution, pinned dispatcher, redirect rejection, timeouts, response bounds and schemas are present. |
+| precise placement | **Mostly confirmed** | mouse, touch tolerance, keyboard, occupied-cell validation, dynamic bounds, nearest fit and immediate persistence exist. Failure/race/live responsive coverage remains insufficient. |
+| widget catalog documentation | **Partial** | all kinds have pages and shared interactions; wording overclaims focus behavior and validation counts are stale. |
+| validation: 40 files/162 tests, 6/6 E2E | **Stale** | current diff contains 57 changed unit-spec files (including three E2E specs); repository has 42 widget spec files and six E2E specs. Update from the exact final run. |
+| independent reviews “pass” | **Not supported** | unresolved valid findings, current P1s, and unavailable live QA contradict an unqualified pass. |
+
+## Mantine suitability ledger
+
+Legend: **U** already used appropriately; **C** consolidation/replacement candidate; **R** relevant but current implementation can stay; **N** not relevant to widget runtime; **D** dependency/version addition. Exact 9.4.1 package archives were inspected in `/tmp`; live docs/Context7 include 9.5.0.
+
+### Core catalog
+
+- **U:** ActionIcon, Alert, Anchor, Avatar, Badge, Box, Button, Card, Center, Code, Divider, Flex, Grid, Group, Image, Loader, Menu (`Menu.ContextMenu`), Overlay, Paper, Popover, Portal, Progress, RingProgress, ScrollArea, SimpleGrid, Skeleton, Stack, Table, Tabs, Text, ThemeIcon, Title, Tooltip, UnstyledButton.
+- **C:** Accordion (dense integration sections), DataList (semantic label/value stats), EmptyState (replace bespoke empty state), FocusTrap and Modal compound API (WID-002), LoadingOverlay (background refetch without deleting content), NumberFormatter, OverflowList/Scroller (responsive action and chip rows), Table.ScrollContainer, Timeline (activity/feed histories), VisuallyHidden and Kbd, CloseButton, Dialog/HoverCard/Notification where persistent errors/actions need semantics.
+- **R:** Affix, AppShell, AspectRatio, BackgroundImage, Breadcrumbs, Burger, Checkbox, Chip, Collapse, ColorInput/ColorPicker/ColorSwatch/AlphaSlider/AngleSlider/HueSlider, Combobox/ComboboxPopover, Container, CopyButton, Drawer, Fieldset, FileButton/FileInput, FloatingIndicator/FloatingWindow, Highlight, Indicator, Input/JsonInput, List, Mark, Marquee, MaskInput, Menubar, MultiSelect/NativeSelect/NumberInput/PasswordInput/PillsInput/PinInput/Radio/RangeSlider/Rating/SegmentedControl/Select/Slider/Switch/TagsInput/Textarea/TextInput, Pagination, Pill, SemiCircleProgress, Space, Splitter, Spoiler, Stepper, Transition, Tree/TreeSelect, Typography. Use only where an existing widget requirement matches; do not churn working layouts.
+- **N:** Alpha/color/angle primitives outside configuration forms, AppShell/navigation primitives, TableOfContents, CorePackage documentation.
+- **D (9.5, not 9.4.1):** Cascader. **RollingNumber and Splitter are present in 9.4.1.**
+
+### Hooks catalog
+
+- **C:** `useFocusReturn`, `useFocusTrap`, `useReducedMotion`, `useRovingIndex`, `useLongPress`, `useScroller`, `useElementSize`, `useViewportSize`, `useHotkeys`, `useMediaQuery`, `useResizeObserver`.
+- **U/R:** focus-within, hover, disclosure, click-outside, merged-ref, interval/timeout, local-storage, event/window listeners, throttled/debounced values and list/set/map/state helpers are available in 9.4.1 and should replace local mechanics only when behavior and tests improve.
+- **N:** document title/favicon/hash/headroom/page-leave/OS/eye-dropper/fullscreen/network/orientation and other page-level utilities have no direct widget-remediation need. Every hook listed under `## Hooks` in `MANTINE.md` is covered by this rule; no hook requires 9.5 for the recommendations above.
+
+### Form, dates, charts, schedule, extensions, theme and styles
+
+- **Form:** existing `@homarr/form` should remain the abstraction over all cataloged form APIs. No widget-runtime replacement; use current validation/status APIs for widget editors (**R**).
+- **Dates:** all catalog entries are in installed 9.4.1 dates. `Calendar`, `MiniCalendar`, and `TimeValue` are candidates; other picker/input entries are editor-only (**R**). Importing dates directly into `@homarr/widgets` requires adding that workspace dependency (**D**).
+- **Charts:** 9.4.1 exports Area, Bar, BarsList, Bubble, Composite, Donut, Funnel, Heatmap, Line, Pie, Radar, RadialBar, Sankey, Scatter, Sparkline and Treemap (**C/R**). Prefer Sparkline for tiny trends, BarsList for ranked values, Heatmap for calendar/activity density, Composite for mixed analytics. **BulletChart and SunburstChart are 9.5-only (D)**. Replacement requires accessibility and bundle comparison, not blanket Recharts removal.
+- **Schedule:** all `AgendaView`, day/week/month/year/resource views and Schedule pages require new `@mantine/schedule` dependency (**D**). AgendaView is the only plausible calendar-widget candidate; its weight and semantics must beat the existing grouped list before adoption.
+- **Guides/FAQ/Other:** server components, portal testing, scroll lock, accessibility, nested popovers, zoom, responsive styling and version changelogs are relevant references (**R**); framework/setup/contribution/old migration entries are **N**. The 9.5 changelog is informational only.
+- **Theming/styles:** Colors, ColorSchemes, DefaultProps, MantineProvider, ThemeObject, CSS variables, data attributes, CSS Modules, responsive/RTL/style props/Styles API/performance are **U/R**. WID-009 requires shared layer/color tokens. Emotion, Sass, Vanilla Extract and unrelated global setup are **N**.
+- **Extensions:** existing modals, notifications, spotlight and rich-text abstractions are **U**; Carousel could consolidate Immich only after behavior parity; Dropzone/CodeHighlight/NProgress are **N** for this audit.
+
+## Complete 54-widget coverage matrix
+
+“Test” means a focused widget-local spec exists, not that all required states are covered. Every row still requires the common contract suite in WID-013.
+
+| WidgetKind | Family | Advanced | Focused test | Principal audit gap |
+| --- | --- | --- | --- | --- |
+| clock | basic | yes | no | query error, locale-format options |
+| weather | basic/feed | yes | yes | query error; dayjs locale/time patterns |
+| app | basic/link | **no** | no | PR wording; URL contract |
+| iframe | custom content | yes | yes | manual focus/iframe trapping; URL privacy thread now fixed |
+| video | custom content | yes | no | media/focus/live behavior |
+| notebook | notes | yes (inner) | yes | editor focus inside manual surface |
+| anchorNote | notes | yes | no | query error, save/error regression |
+| dnsHoleSummary | network | yes | no | total query error, hard-coded chart colors |
+| dnsHoleControls | network/security | yes | no | total error and permission matrix |
+| smartHome-entityState | smart home | yes | no | raw state in aria; action permissions |
+| smartHome-executeAutomation | smart home | yes | no | mutation/permission/edit-mode E2E |
+| stockPrice | analytics | yes | yes | total error |
+| mediaServer | media | yes | no | total error and table a11y |
+| calendar | calendar | yes | yes | total error; fixed HH:mm; schedule tradeoff |
+| downloads | media | yes | yes | four-state/component coverage, arbitrary z-index |
+| mediaRequests-requestList | media | yes | no | total error; external URLs/actions |
+| mediaRequests-requestStats | media | yes | no | total error; links/colors |
+| mediaTranscoding | media | yes | yes | total error and responsive table |
+| mediaMissing | media | yes | yes | total error and external URL |
+| minecraftServerStatus | infrastructure | yes | no | total error/malformed payload |
+| networkControllerSummary | networking | yes | no | total error; WAN/unknown localization |
+| networkControllerStatus | networking | yes | no | responsive rule duplication |
+| rssFeed | feed | yes | yes | strong stale/partial state; locale relative time |
+| bookmarks | basic/link | yes | yes | recomputation; URL contract |
+| indexerManager | media | yes | no | total error; runtime action ref |
+| healthMonitoring | infrastructure | yes | yes | integration query error |
+| releases | infrastructure/feed | yes | yes | secret/item authorization needs E2E |
+| mediaReleases | media | yes | no | total error; external URLs |
+| dockerContainers | infrastructure | yes | yes | total error; z-index/table semantics |
+| firewall | networking/security | yes | yes | partial-query matrix; loose router generics |
+| notifications | infrastructure | yes | no | total error; untrusted href |
+| systemResources | infrastructure | yes | yes | total error; previous network thread fixed |
+| coolify | infrastructure | yes | yes | total error; URL validation |
+| systemDisks | infrastructure | yes | yes | total error |
+| timetable | calendar | yes | no | backend strong; component locale/live tests absent |
+| immich-serverStats | media | yes | yes | two ignored query errors |
+| immich-albumCarousel | media | yes | yes (query) | query error and nested overlay/focus E2E |
+| paperlessNgx | analytics | yes | no | former error thread fixed, no regression spec |
+| patchmon | infrastructure | yes | yes | total error |
+| bazarr | media | yes | yes | total error |
+| tracearr | analytics | yes | yes | total error |
+| speedtestTracker | analytics | yes | yes | total error |
+| uptimeKuma | infrastructure | yes | yes | total error |
+| audioStats | media | yes | yes | good dual-error handling; partial-state tests needed |
+| umami | analytics | yes | no | runtime query registration; chart a11y |
+| vpn | networking/security | yes | no | total error; status/permission matrix |
+| archiveTeamWarrior | infrastructure | yes | yes | suspense path; docs present |
+| ups | infrastructure | yes | no | total error |
+| beszelSystemTable | monitoring | yes | yes | nested chart/focus and live subscription |
+| beszelSystemGrid | monitoring | yes | no | live/error UI; no focused renderer test |
+| beszelAlerts | monitoring | yes | no | partial error and status semantics |
+| beszelSystemStats | monitoring | yes | yes | runtime refs, locale tooltip |
+| traefik | networking | yes | no | total error; bounded backend positive |
+| customApi | custom content/security | yes | yes | WID-001/007/008; polling resume fixed |
+
+## Severity-ranked findings
+
+### WID-001 — P1 confirmed, pre-existing: Custom API actions lack resource authorization and hardened fetch policy
+
+- **Location/claim:** `packages/api/src/router/custom-widget/custom-widget-router.ts:57-102,392-451`; “harden Custom API” and action-permission semantics.
+- **Evidence/reproduction:** any authenticated account can call `customWidget.all`, learn every definition ID and URL, then call `customWidget.execute({definitionId})`. The mutation verifies neither board nor item nor an action permission. It decrypts administrator-provided secrets, uses `new URL()` only, and `redirect: "follow"`. The PR hardened `widget.customApi.getData`, but not this sibling execution path.
+- **Impact/rule:** unauthorized side effects against internal services and authenticated redirect chains; broken object-level authorization. A view-only account can invoke administrator-created controls outside any board context.
+- **Correction:** require `{boardId,itemId,definitionId}`; load the item and verify it is `customApi`, belongs to the board, references the definition, and the caller has an explicit execute policy. Until that policy exists, restrict to admin. Share `validateCustomApiUrl`, timeout, redirect rejection, bounded disposal and redacted errors with getData. Compatibility: old direct callers must send item/board context.
+- **Tests:** procedure tests for anonymous, unrelated authenticated, view-only, modifier, admin, mismatched item/definition, disabled definition, redirects, credential URL, timeout and secret non-disclosure; E2E action button across roles.
+
+### WID-002 — P1 confirmed, PR-introduced: manual advanced focus is not an accessible modal
+
+- **Location:** `advanced-focus/context.tsx:194-209`, `items/item-content.tsx:113-189`.
+- **Evidence:** manual mode renders an Overlay plus a `role="region"` Card. There is no FocusTrap, `aria-modal`, background inertness, scroll lock or backdrop dismissal. Only the close button is initially focused; Tab can leave the surface and activate dimmed board controls.
+- **Impact/rule:** keyboard/screen-reader users lose the interaction boundary; WCAG 2.1.2, 2.4.3 and ARIA dialog pattern. Iframes/editors make escape especially likely.
+- **Correction/Mantine:** render manual mode through `Modal.Root` compound parts (or ModalBase), using its portal, FocusTrap, scroll lock, Escape/return-focus and semantic content. Preserve preview mode as non-modal. Configure nested overlays and `removeScrollProps`; keep a 44px `Modal.CloseButton` and explicit title via VisuallyHidden if needed.
+- **Tests:** RTL portal/focus-cycle/return tests; Playwright Tab/Shift+Tab, nested Menu/Select/Popover/editor/iframe, backdrop and every close path; reduced motion and 200% zoom.
+
+### WID-003 — P1 probable, PR-introduced: fixed advanced surface remains under transformed Gridstack ancestry
+
+- **Location:** `advanced-focus.module.css:35-42`; Card remains inside the Gridstack item while only the backdrop is portaled.
+- **Evidence:** CSS fixed descendants establish their containing block at a transformed ancestor. Gridstack uses transforms for positioned items, so viewport-derived `left/top` can be interpreted relative to the item on affected layouts. A Portal wrapper exists only for Overlay.
+- **Impact:** off-screen, clipped or incorrectly scaled focus surface across breakpoints/remounts.
+- **Correction:** portal the focused surface itself (naturally achieved by WID-002) and calculate geometry in the same viewport coordinate system. Do not compensate with transform-specific offsets.
+- **Tests:** real Gridstack at every layout/viewport, scrolled page, header, dynamic section and resize/remount; assert bounding rect within viewport and finite scale.
+
+### WID-004 — P1 confirmed, regression-exposed/systemic: query failures still become valid empty/disconnected UI
+
+- **Location:** 39 of 53 renderer query calls do not read `error`, including bookmarks, weather, calendar, media server, system resources, Coolify, DNS Hole, Tracearr, notifications, Immich, VPN and others listed in the matrix. Query defaults do not set `throwOnError` (`trpc.tsx:69-93`).
+- **Evidence:** TanStack Query returns `data=undefined` on total failure. Components default to `[]`, `null`, or empty branches, so the board ErrorBoundary is never reached. Network Controller and Paperless were fixed, proving the pattern; it was not completed.
+- **Impact:** operators are told “empty” or “disconnected” when monitoring failed, violating the product’s truthful-state requirement.
+- **Correction/Mantine:** introduce a shared `WidgetQueryState` contract distinguishing initial Skeleton, background LoadingOverlay, stale Alert, partial warning, true EmptyState and terminal error. Either explicitly throw only when no usable cached data or render a shared error Alert. Preserve stale data.
+- **Tests:** parameterized contract tests for every queried widget: initial, success-empty, total failure, stale+failure, partial failure, refetch, remount/persisted cache and sibling isolation.
+
+### WID-005 — P1 confirmed, PR-introduced/systemic: integration-provided links bypass a shared URL policy
+
+- **Location:** examples include `media-missing/component.tsx:291`, `notifications/component.tsx:77`, media requests list/stats, Coolify resource/server links, releases, bookmarks and iframe. Only RSS and custom JSX visibly use explicit protocol sanitizers.
+- **Evidence:** dynamic strings are assigned directly to `href`/new-tab controls. Integration APIs and remote feeds are trust boundaries; no renderer-wide `http/https` guard is enforced.
+- **Impact:** unsafe schemes, credential-bearing URLs, internal detail disclosure and inconsistent opener behavior.
+- **Correction:** add one typed application URL helper returning a sanitized URL/undefined, with allowed schemes per use case, credential stripping/rejection, consistent `noopener noreferrer`, and disabled non-link presentation. Validate again server-side for action endpoints.
+- **Tests:** `javascript:`, `data:`, malformed, credential, relative, http/https and missing URLs for each shared link primitive.
+
+### WID-006 — P2 confirmed, PR-introduced: runtime query/action state mutates untyped refs during render
+
+- **Location:** `definition.ts:121-162`; runtime registration calls in media missing/transcoding, audio, calendar, Umami, Beszel and Immich; `widget-context-menu.tsx:157-174`.
+- **Evidence:** `setWidgetRuntimeQueries` mutates a shared object during render; concurrent/aborted renders can publish uncommitted query identity. Action dispatch casts the definition to `Record` and `any`, then casts its result back.
+- **Correction:** make definition generics preserve kind/options/action types end-to-end; replace stringly opaque ref slots with a typed per-item registration API updated in a committed effect (or use `useSyncExternalStore`). Separate query registration from imperative actions.
+- **Tests:** StrictMode, interrupted/remount, options change and cleanup; compile-time `tsd`/type assertions rejecting wrong action props.
+
+### WID-007 — P2 confirmed, regression-exposed: raw backend errors are exposed to users and assistive text
+
+- **Location:** Custom API UI `component.tsx:561-576`; API `custom-api.ts:79-85`; several partial fallbacks use `error.message`.
+- **Impact:** internal hostnames, request paths and implementation details can appear in tooltips/aria labels and logs.
+- **Correction/Mantine:** return stable error codes plus redacted public messages; keep detailed causes server-side with correlation IDs. Render localized Alert/Notification details, never raw throwable text.
+- **Tests:** errors containing credentials, query strings, private hosts and multiline backend bodies must not reach tRPC payload, DOM, accessibility tree or notifications.
+
+### WID-008 — P2 confirmed, PR-introduced/regression-exposed: locale handling remains incomplete
+
+- **Locations:** Beszel chart/tooltip dayjs formats, calendar event `HH:mm`, downloads `YYYY-MM-DD HH:mm`, timetable `HH:mm:ss`, weather day/month/time formats; `_inputs/widget-location-input.tsx:199` hardcodes `Intl.NumberFormat("en")`.
+- **Also:** `beszel/_shared/disk-usage.tsx:41`, stats loading aria label, Network Controller `WAN`/`unknown` are English/raw accessible strings.
+- **Correction:** centralize app-locale date/time/number/duration/status helpers; load canonical dayjs locale only at the boundary; use localized translation keys for aria/status. NumberFormatter is suitable for simple display but application helpers should own locale.
+- **Tests:** at least `en-US`, `fr-FR`, `de-DE`, RTL locale, 12/24-hour preference and long translations, including accessibility names.
+
+### WID-009 — P2 confirmed: layer and color tokens are inconsistent
+
+- **Locations:** downloads z-index 1000 and rgba row colors; item title z-index 4; item menu/dynamic menu 10; media server white rgba; Indexer Manager hex colors; DNS Hole rgba palette; Umami hex tick colors.
+- **Correction:** define shared semantic layer tokens relative to Mantine CSS z-index variables and semantic status/chart tokens respecting light/dark/forced colors. Do not replace intentional service branding.
+- **Tests:** light/dark/forced-colors with nested overlays and tooltip ordering.
+
+### WID-010 — P2 confirmed: responsive decisions are duplicated and under-tested
+
+- **Location:** Network Controller Status `component.tsx:44,59-78` (open valid thread); Bookmark `component.tsx:94` recomputation (valid but low-impact thread); similar width/height literals across renderers.
+- **Correction:** small typed breakpoint lookup helpers per family, memoized only for genuinely expensive layout. Prefer container-query Grid/SimpleGrid where structural. Do not cargo-cult `useMemo` for a cheap pure calculation; benchmark Bookmark before accepting the review suggestion.
+- **Tests:** boundary tables for minimum/typical/wide/short/advanced sizes and 200% zoom.
+
+### WID-011 — P2 confirmed: PR/docs overstate behavior and validation
+
+- **Location:** PR body, board/getting-started copy “advanced preview in place,” validation numbers, and unqualified review pass.
+- **Correction:** state 53 advanced renderers plus compact-only app; call manual focus an overlay only after WID-002/003; publish exact final-SHA check/test counts and disclose unsupported/live-untested states.
+
+### WID-012 — P2 confirmed: focused automated coverage is far below the 54-widget/data-state claim
+
+- **Evidence:** 42 widget-local spec files are helper-heavy; 20+ renderers have no focused spec. Six E2E files exist, but only board interactions and Custom JSX directly exercise this modernization. The earlier Custom JSX login wait failed once and current exact-route wait remains.
+- **Correction:** add shared renderer contract harness plus family E2Es. Replace Custom JSX’s exact post-submit URL race with a shared login helper that waits for authenticated session/stable destination, then run it three times in CI.
+
+### WID-013 — P2 confirmed: open review-thread state no longer matches code
+
+| Thread | Classification |
+| --- | --- |
+| iframe complete URL | already fixed/false positive at current line: only host is rendered |
+| system resources network gate | already fixed, outdated |
+| RSS total error | already fixed: no-data error throws; stale data retained |
+| bookmarks memoization | valid P3/P2-low; benchmark first |
+| network-controller total error | already fixed, outdated |
+| network-controller breakpoint consolidation | valid P2 maintainability |
+| Paperless total error | already fixed, outdated |
+
+Resolve the five stale threads with evidence; address or explicitly disposition the two live suggestions. Re-run review because CodeRabbit was paused during active development.
+
+## Systemic remediation roadmap
+
+1. **Security/authorization:** WID-001 and WID-005; add negative-role/protocol tests before merge.
+2. **Interaction accessibility:** replace manual focus with portaled Mantine modal semantics (WID-002/003), then test nested widget content.
+3. **Truthful data state:** build the shared query-state contract and migrate all 39 ambiguous sites; keep valid stale data and partial results.
+4. **Typed interfaces:** replace `widgetStateRef` slot mutation and `any` context dispatch with committed typed registrations.
+5. **Widget families:** media/downloads, infrastructure, networking/security, smart-home, analytics/calendar/feed, then Beszel; apply URL, state, locale, responsive and a11y contracts in that order.
+6. **Design-system cleanup:** semantic layers/colors and restrained Mantine consolidation. Do not add Schedule or upgrade to 9.5 without a separate dependency/bundle decision.
+7. **Docs/PR:** correct renderer count, behavior and exact validation evidence.
+8. **Final proof:** frozen install on supported Node/pnpm; format, lint, full typecheck, affected and full unit tests, production and docs builds; Custom JSX three times; complete production-container E2E; desktop/tablet/mobile keyboard/touch/light/dark/reduced-motion/zoom/a11y-tree QA. Push nothing until all refer to one unchanged SHA; close every review thread and obtain required approval.
+
+## Positive findings
+
+- Timetable is the strongest security work in the PR: saved resource authorization, private-range rejection, DNS rebinding resistance through pinned lookup, redirect rejection, bounds and schemas are coherently tested.
+- Bounded response helpers, Beszel bounded queue/cancellation, and partial-integration settlement materially reduce failure blast radius.
+- RSS preserves stale data and distinguishes partial feed failure; Network Controller and Paperless now correctly escalate total failures.
+- Item-scoped query matchers and tests address sibling-widget refresh isolation; global polling defaults are restricted to the widget namespace.
+- Touch long-press has movement tolerance; focus scale guards prevent Infinity/NaN; reduced-motion and remount cleanup are present.
+- Existing Homarr theme, Mantine components and shared UI vocabulary are generally preserved. Most proposed Mantine work is consolidation, not redesign.
+
+## Validation evidence and uncertainty
+
+- `MANTINE.md` was fetched from `https://mantine.dev/llms.txt` and passed byte-for-byte `cmp`; `llms-full.txt` stayed in `/tmp`.
+- Context7 current Mantine docs were queried for overlay/focus, responsive/data-state/accessibility, charts and Schedule. Static 9.4.1 archives verified exact exports; Cascader, BulletChart and SunburstChart are 9.5-only.
+- Local frozen install could not start because host Node/pnpm are below repository engine requirements. No dependencies, source files or machine toolchain were changed.
+- Docker API access is denied, so production-container E2E could not be rerun locally. Hosted exact-head Fast gate and Container/E2E are green.
+- `agent-browser` is not installed. Temporary `npx` execution was rejected by the environment’s security reviewer, so live exploratory QA/a11y-tree inspection was not performed.
+- External integrations were not available; malformed/partial/permission combinations are static-analysis conclusions unless a focused test already exists.
+- This review intentionally changes no production code and makes no commit, push, thread resolution or PR edit.
+
+Finalization must re-query the PR head/checks/threads. If the head differs from the reviewed SHA, this report is a snapshot and the delta requires review before merge.
+
+**Final refresh (2026-08-03):** head remained `96e92485a5fd45f3648cfac0453e614779c414ba`; all reported hosted checks remained successful; merge state remained `BLOCKED` with no approval decision; unresolved threads remained 7 of 34. The branch did not change during the audit.
diff --git a/apps/docs/.vscode/settings.json b/apps/docs/.vscode/settings.json
index c5a1b2892..2eef88bf3 100644
--- a/apps/docs/.vscode/settings.json
+++ b/apps/docs/.vscode/settings.json
@@ -1,10 +1,3 @@
{
- "cSpell.words": [
- "Homarr",
- "Lidarr",
- "Nextcloud",
- "Radarr",
- "Readarr",
- "Sonarr"
- ]
-}
\ No newline at end of file
+ "cSpell.words": ["Homarr", "Lidarr", "Nextcloud", "Radarr", "Readarr", "Sonarr"]
+}
diff --git a/apps/docs/README.md b/apps/docs/README.md
index b66530488..ede750aa7 100644
--- a/apps/docs/README.md
+++ b/apps/docs/README.md
@@ -21,8 +21,14 @@ pnpm dev:docs
# Or from within this directory
pnpm dev
+
+# Point the docs at a local PocketBase instance
+WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
```
+The standalone docs use `WORKSHOP_API_URL` for PocketBase and `HOMARR_WEBSITE_URL` for the public site origin. The old
+`WORKSHOP_URL` name is a deprecated one-release alias.
+
## Build
```bash
@@ -40,7 +46,7 @@ Uses oxlint and oxfmt (same as the rest of the monorepo):
```bash
pnpm lint # oxlint
pnpm format # oxfmt (check)
-pnpm format -- --write # oxfmt (fix)
+pnpm format:fix # oxfmt (fix)
```
## Typecheck
@@ -71,7 +77,7 @@ Each integration/widget doc follows a consistent pattern:
The docs app has `@homarr/definitions` as a workspace dependency. You can import integration kinds, widget kinds, and other definitions directly:
```typescript
-import { IntegrationKind } from '@homarr/definitions';
+import { IntegrationKind } from "@homarr/definitions";
```
This enables type-safe references to homarr domain concepts within documentation code.
@@ -90,15 +96,15 @@ pnpm verify:search
## Tech Stack
-| Layer | Technology |
-|---|---|
+| Layer | Technology |
+| -------------- | ------------------------------------------- |
| Site generator | Docusaurus 3 (`@docusaurus/preset-classic`) |
-| Styling | Tailwind CSS 3 + Docusaurus Infima |
-| Icons | `@tabler/icons-react` |
-| Diagrams | Mermaid (`@docusaurus/theme-mermaid`) |
-| Search | Algolia DocSearch |
-| Analytics | PostHog |
-| Charts | @nivo/line |
+| Styling | Tailwind CSS 4 + Docusaurus Infima |
+| Icons | `@tabler/icons-react` |
+| Diagrams | Mermaid (`@docusaurus/theme-mermaid`) |
+| Search | Algolia DocSearch |
+| Analytics | PostHog |
+| Charts | @nivo/line |
## Contributing
diff --git a/apps/docs/components.json b/apps/docs/components.json
new file mode 100644
index 000000000..15e244db4
--- /dev/null
+++ b/apps/docs/components.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "base-nova",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "src/css/custom.css",
+ "baseColor": "mist",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "tabler",
+ "rtl": false,
+ "menuColor": "default",
+ "menuAccent": "subtle"
+}
diff --git a/apps/docs/docs/advanced/command-line/_category_.json b/apps/docs/docs/advanced/command-line/_category_.json
index f3ee30aa5..4b4aec4c3 100644
--- a/apps/docs/docs/advanced/command-line/_category_.json
+++ b/apps/docs/docs/advanced/command-line/_category_.json
@@ -1,4 +1,4 @@
{
"collapsed": true,
"label": "Command line interface"
-}
\ No newline at end of file
+}
diff --git a/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx b/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx
index d3b9f50cc..bf8808118 100644
--- a/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx
+++ b/apps/docs/docs/advanced/command-line/fix-usernames/index.mdx
@@ -14,4 +14,4 @@ homarr fix-usernames
This will convert all credential usernames to lowercase and update the database accordingly.
-
\ No newline at end of file
+
diff --git a/apps/docs/docs/advanced/command-line/index.mdx b/apps/docs/docs/advanced/command-line/index.mdx
index 0e3e802f7..287985739 100644
--- a/apps/docs/docs/advanced/command-line/index.mdx
+++ b/apps/docs/docs/advanced/command-line/index.mdx
@@ -1,7 +1,7 @@
# Command line interface
The Homarr image ships with a built-in recovery tool. This can be useful, if you do not have access to Homarr or if something is not working.
-Please note that the CLI is an *emergency tool*, not an API tool.
+Please note that the CLI is an _emergency tool_, not an API tool.
:::caution
@@ -12,10 +12,10 @@ You should also create a backup of the database, in case you break something.
## Execute CLI with Docker
-1. Execute ``docker ps | grep homarr`` on the root system with Docker to find the container ID of your Homarr container.
-2. Run ``docker exec -it /bin/bash homarr ``. \
-Replace ```` with your container ID from step 1. \
-Replace ```` with the command you want to execute (see sub-pages).
+1. Execute `docker ps | grep homarr` on the root system with Docker to find the container ID of your Homarr container.
+2. Run `docker exec -it /bin/bash homarr `. \
+ Replace `` with your container ID from step 1. \
+ Replace `` with the command you want to execute (see sub-pages).
## Execute CLI with QNAP
diff --git a/apps/docs/docs/advanced/command-line/integrations/index.mdx b/apps/docs/docs/advanced/command-line/integrations/index.mdx
index dfdb587b0..b958e9d2b 100644
--- a/apps/docs/docs/advanced/command-line/integrations/index.mdx
+++ b/apps/docs/docs/advanced/command-line/integrations/index.mdx
@@ -7,6 +7,7 @@ This guide requires that you understand [how to access the CLI](../).
:::
Using these commands, you can manage the integrations from the CLI:
+
```bash
Group of commands to manage integrations
@@ -43,7 +44,7 @@ Flags:
## Delete Integration
- Requires a database connection
-- Requires one parameter: ``id`` or ``name``
+- Requires one parameter: `id` or `name`
- Deletes an integration permanently from the database
```bash
@@ -57,4 +58,4 @@ Flags:
-n, --name string Name of the integration
-h, --help help for integrations delete
-v, --version version for homarr-cli
-```
\ No newline at end of file
+```
diff --git a/apps/docs/docs/advanced/command-line/password-recovery/index.mdx b/apps/docs/docs/advanced/command-line/password-recovery/index.mdx
index e5a80f669..844b44140 100644
--- a/apps/docs/docs/advanced/command-line/password-recovery/index.mdx
+++ b/apps/docs/docs/advanced/command-line/password-recovery/index.mdx
@@ -12,8 +12,8 @@ All sessions for that user will be terminated and the user must re-login.

-Using the ``--username`` parameter, you can specify who you want to reset the password for.
+Using the `--username` parameter, you can specify who you want to reset the password for.
Executing the command will create a new random password, update the database and display the new password.
All sessions for this user will be terminated and they must re-login.
-
\ No newline at end of file
+
diff --git a/apps/docs/docs/advanced/command-line/users/index.mdx b/apps/docs/docs/advanced/command-line/users/index.mdx
index a867fa72d..93170259a 100644
--- a/apps/docs/docs/advanced/command-line/users/index.mdx
+++ b/apps/docs/docs/advanced/command-line/users/index.mdx
@@ -7,6 +7,7 @@ This guide requires that you understand [how to access the CLI](../).
:::
Using these commands, you can manage the users from the CLI:
+
```bash
Group of commands to manage users
@@ -42,7 +43,7 @@ yj52ek5cyb6du4bp4osbkjry admin xxxx@example.com
## Delete User
- Requires a database connection
-- Requires one parameter: ``id`` or ``username``
+- Requires one parameter: `id` or `username`
```bash
Delete a user by id or username
@@ -76,4 +77,4 @@ Flags:
-p, --password New password
-h, --help help for users update-password
-v, --version version for homarr-cli
-```
\ No newline at end of file
+```
diff --git a/apps/docs/docs/advanced/development/_category_.json b/apps/docs/docs/advanced/development/_category_.json
index 9f659a536..bf0464672 100644
--- a/apps/docs/docs/advanced/development/_category_.json
+++ b/apps/docs/docs/advanced/development/_category_.json
@@ -1,9 +1,8 @@
{
- "label": "Developer guides",
- "position": 6,
- "collapsed": true,
- "link": {
- "type": "generated-index"
- }
+ "label": "Developer guides",
+ "position": 6,
+ "collapsed": true,
+ "link": {
+ "type": "generated-index"
}
-
\ No newline at end of file
+}
diff --git a/apps/docs/docs/advanced/development/getting-started.mdx b/apps/docs/docs/advanced/development/getting-started.mdx
index e8f68ea6d..de119072c 100644
--- a/apps/docs/docs/advanced/development/getting-started.mdx
+++ b/apps/docs/docs/advanced/development/getting-started.mdx
@@ -8,70 +8,121 @@ tags:
sidebar_position: 1
---
-## Setting up your development environment
-
-This guide will help you set up your development environment to start contributing to homarr.
+## Set up your development environment
### Prerequisites
-- **Node.js (lts)**: You can download it from the [official website](https://nodejs.org/).
-- **pnpm (latest)**: You can install it by running `npm install -g pnpm`.
+- **Node.js 24.18 or newer**: You can download it from the [official website](https://nodejs.org/).
+- **Corepack**: Run `corepack enable` so the repository selects its pinned pnpm 11 version.
- **Git**: You can download it from the [official website](https://git-scm.com/).
- **Docker or Docker Desktop**: Required for the Redis service and the developer Docker CLI.
- **Go 1.25+**: Required only for the developer Docker CLI. You can download it from the [official website](https://go.dev/).
- **GitHub CLI**: Required only for pull-request image features. Install it from the [official website](https://cli.github.com/), then authenticate with `gh auth login`.
-### Setting up the project
+The versions in the root `package.json` are the supported baseline. Let Corepack select the repository version instead
+of installing an unrelated global pnpm:
-1. Fork the repository by clicking on the "Fork" button on the top right corner of the repository page.
+```sh
+corepack enable
+corepack install
+pnpm install --frozen-lockfile
+```
-
+### Run the Homarr application
-2. Clone the repository to your local machine with `git clone`. You can find the url to use by clicking on the "Code" button on the top right corner of your forks repository page.
+1. Copy `.env.example` to `.env`.
+2. Set `DB_URL` to an absolute writable SQLite file path.
+3. Keep Redis and the optional local databases running in one terminal.
+4. Apply migrations and start Next.js in another terminal.
-
+```sh
+cp .env.example .env
+```
-3. Change directory to the project folder with `cd homarr`.
+Terminal 1:
-4. Install the dependencies with `pnpm install`.
+```sh
+pnpm docker:dev
+```
-5. Create `.env` from `.env.example`. In a POSIX shell, run `cp .env.example .env`; in PowerShell, run `Copy-Item .env.example .env`. Set `DB_URL` to an absolute path for your SQLite file.
+Terminal 2:
-6. Start Redis in the background with `pnpm docker:dev:up` if your development workflow needs it.
+```sh
+pnpm db:migration:sqlite:run
+pnpm dev
+```
-7. Create or update the database with `pnpm db:migration:sqlite:run`. This also seeds the default data. To run the seed explicitly later, use `pnpm db:seed`.
+The infrastructure command remains in the foreground. Open `http://127.0.0.1:3000`; the first user created through
+Homarr becomes the instance administrator.
+Use `pnpm docker:dev:up` instead when you only need Redis in the background. Running the migration also seeds the
+default data; use `pnpm db:seed` to seed it explicitly later.
-8. Start the development server with `pnpm dev` and wait for a message that says `✓ Ready in 10.7s`
+### Run PocketBase and documentation
-9. Open your browser and navigate to [http://localhost:3000](http://localhost:3000). (Loading the page might take up to 2 minutes the first time)
+Start PocketBase in another terminal:
-### Useful npm scripts
+```sh
+cp apps/workshop/.env.example apps/workshop/.env
+docker compose --env-file apps/workshop/.env -f apps/workshop/docker-compose.yml up --build workshop
+```
-#### During development
+Then start Docusaurus in a third terminal:
-- `pnpm dev`: Start the development server.
-- `pnpm dev:cli -- dev`: Browse and run local images and remote pull-request images.
-- `pnpm dev:cli -- build `: Build the current checkout as `homarr:`.
-- `pnpm dev:cli -- build --pr `: Build a pull request locally from a temporary checkout.
-- `pnpm dev:cli -- rebuild `: Rebuild a local image from its recorded checkout or pull request.
-- `pnpm dev:cli:install`: Optionally install the developer CLI as a `homarr` binary.
-- `pnpm cli`: Run cli commands.
-- `pnpm db:migration:sqlite:run`: Run the migrations to create or update the sqlite db file.
-- `pnpm db:seed`: Explicitly seed the default database data.
-- `pnpm docker:dev:up`: Start the Redis development service in the background.
-- `pnpm db:migration:sqlite:generate`: Generate a new migration file for the sqlite db. Also works with `mysql`.
-- `pnpm db:studio`: Open a visual studio for the database.
-- `pnpm package:new`: Create a new package in the monorepo.
+```sh
+WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs
+```
-#### Checking code quality
+PocketBase runs on port 8090 and Docusaurus on port 3003. Stopping the services preserves the PocketBase volume.
-- `pnpm lint`: Run the linter to check for code quality issues.
-- `pnpm format:fix`: Run the formatter to fix code style issues.
-- `pnpm test`: Run the tests. You can also use `pnpm test:ui` to get a UI for the tests.
-- `pnpm typecheck`: Run the typescript type checker.
+Open `http://127.0.0.1:8090/_/` and create the first PocketBase superuser. The Workshop UI is available at
+`http://127.0.0.1:3003/workshop`.
-#### Building the project
+Point Homarr at the local services with:
-- `pnpm build`: Build the project for production.
-- `pnpm dev:cli -- build `: Build a `homarr:` image and record its checkout for later rebuilds.
-- `docker run -p 7575:7575 -e SECRET_ENCRYPTION_KEY='your_64_character_hex_string' homarr:`: Run the created docker image.
+```dotenv
+HOMARR_WEBSITE_URL=http://127.0.0.1:3003
+WORKSHOP_API_URL=http://127.0.0.1:8090
+WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop
+```
+
+Restart `pnpm dev` after changing `.env`. GitHub OAuth is optional locally. See
+[`apps/workshop/README.md`](https://github.com/homarr-labs/homarr/blob/HEAD/apps/workshop/README.md) for OAuth,
+Workshop moderator promotion, logs, tests, backup, restore, stopping, and intentional volume reset.
+
+### URL testing
+
+Docusaurus reads `HOMARR_WEBSITE_URL` and `WORKSHOP_API_URL` when it starts or builds. Homarr also reads
+`WORKSHOP_WEB_URL` when its server starts, so these values do not rely on `NEXT_PUBLIC_*` build variables. Test a
+separate API origin before release:
+
+```sh
+HOMARR_WEBSITE_URL=http://127.0.0.1:3003 \
+WORKSHOP_API_URL=http://127.0.0.1:8090 \
+WORKSHOP_WEB_URL=http://127.0.0.1:3003/workshop \
+pnpm dev:docs
+```
+
+### Useful commands
+
+- `pnpm dev` — Next.js on port 3000.
+- `pnpm dev:benchmark` — authenticated-board benchmark; readiness-only `--smoke` runs are not performance claims.
+- `pnpm dev:cli -- dev` — browse and run local images and remote pull-request images.
+- `pnpm dev:cli -- build ` — build the current checkout as `homarr:`.
+- `pnpm dev:cli -- build --pr ` — build a pull request from a temporary checkout.
+- `pnpm dev:cli -- rebuild ` — rebuild an image from its recorded checkout or pull request.
+- `pnpm dev:cli:install` — optionally install the developer CLI as a `homarr` binary.
+- `pnpm dev:docs` — Docusaurus only on port 3003.
+- `pnpm docker:workshop` — local PocketBase through Docker Compose.
+- `pnpm docker:dev:up` — Redis development service in the background.
+- `pnpm cli` — Homarr operations CLI.
+- `pnpm db:migration:sqlite:run` — create or update a SQLite database.
+- `pnpm db:seed` — seed the default database data explicitly.
+- `pnpm db:studio` — inspect the database.
+- `pnpm format` / `pnpm format:fix` — check or apply oxfmt.
+- `pnpm lint` / `pnpm lint:fix` — check or apply oxlint.
+- `pnpm typecheck` — typecheck all workspaces.
+- `pnpm test` — unit and contract suites.
+- `pnpm test:workshop` — disposable Workshop integration suite.
+- `pnpm build` — production monorepo build.
+- `docker build -t homarr .` — build a production Docker image.
+- `docker run -p 7575:7575 -e SECRET_ENCRYPTION_KEY='your_64_character_hex_string' homarr:` — run it.
diff --git a/apps/docs/docs/advanced/development/kubernetes.mdx b/apps/docs/docs/advanced/development/kubernetes.mdx
index e27b3ccbc..ff58b6c0a 100644
--- a/apps/docs/docs/advanced/development/kubernetes.mdx
+++ b/apps/docs/docs/advanced/development/kubernetes.mdx
@@ -132,4 +132,4 @@ This command should return CPU and memory usage statistics for each node.
## Conclusion
-You have successfully set up a multi-node Kubernetes cluster using kind and installed the Metrics Server for monitoring resource usage. This setup is useful for local development and testing Kubernetes workloads before deploying them to a production environment.
\ No newline at end of file
+You have successfully set up a multi-node Kubernetes cluster using kind and installed the Metrics Server for monitoring resource usage. This setup is useful for local development and testing Kubernetes workloads before deploying them to a production environment.
diff --git a/apps/docs/docs/advanced/environment-variables/index.mdx b/apps/docs/docs/advanced/environment-variables/index.mdx
index ad249e734..9caa898a5 100644
--- a/apps/docs/docs/advanced/environment-variables/index.mdx
+++ b/apps/docs/docs/advanced/environment-variables/index.mdx
@@ -25,15 +25,17 @@ This is especially useful for [docker secrets](https://docs.docker.com/engine/sw
Using the `PUID` and `PGID` will require you to set the correct permissions on the mounted volumes and if used the docker socket. See more in the [Running as a different user](/docs/advanced/running-as-different-user) documentation.
:::
-| Environment Variable | Description | Possible values | Default |
-| ------------------------ | ----------- | --------------- | ------- |
-| ``PUID`` | User ID to run the container as | Any valid user ID | ``0`` |
-| ``PGID`` | Group ID to run the container as | Any valid group ID | ``0`` |
-| ``LOG_LEVEL`` | Log level to use | ``debug`` / ``info`` / ``warn`` / ``error`` | ``info`` |
-| ``NO_EXTERNAL_CONNECTION`` | Disables some requests that need internet connection | `true` or `false` | `false` |
-| ``ENABLE_DNS_CACHING`` | Enables dns caching. Enabled by default, but if you experience issues with IPv6 or static IPs, see [#4006](https://github.com/homarr-labs/homarr/issues/4006) or disable this flag | `true` or `false` | `true` |
-
-
+| Environment Variable | Description | Possible values | Default |
+| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------- | ----------------------------- |
+| `PUID` | User ID to run the container as | Any valid user ID | `0` |
+| `PGID` | Group ID to run the container as | Any valid group ID | `0` |
+| `LOG_LEVEL` | Log level to use | `debug` / `info` / `warn` / `error` | `info` |
+| `NO_EXTERNAL_CONNECTION` | Disables Homarr's internet-backed requests, including update checks, weather, and RSS feeds | `true` or `false` | `false` |
+| `ENABLE_DNS_CACHING` | Enables dns caching. Enabled by default, but if you experience issues with IPv6 or static IPs, see [#4006](https://github.com/homarr-labs/homarr/issues/4006) or disable this flag | `true` or `false` | `true` |
+| `HOMARR_DISABLE_IPV6` | Disables the IPv6 listener of the internal nginx proxy, even when the host supports IPv6. IPv6 is normally disabled automatically when the host has no IPv6 configured. See [#4596](https://github.com/homarr-labs/homarr/issues/4596) | `true` or `false` | `false` |
+| `HOMARR_WEBSITE_URL` | Main Homarr documentation/site base used by runtime links. Must be an absolute HTTP(S) URL. | HTTP(S) URL | `https://homarr.dev` |
+| `WORKSHOP_API_URL` | PocketBase API used by Homarr and standalone documentation. | HTTP(S) URL | `HOMARR_WEBSITE_URL` |
+| `WORKSHOP_WEB_URL` | Public Workshop page opened from Homarr. | HTTP(S) URL | `HOMARR_WEBSITE_URL/workshop` |
## Authentication
@@ -43,9 +45,9 @@ See [Single Sign-On](/docs/advanced/single-sign-on) for more informations.
The `SECRET_ENCRYPTION_KEY` is required. If none is specified before starting the container, a random key will be shown in the error message and the container will exit.
-| Environment Variable | Description | Possible values | Default |
-| ------------------------ | ----------- | --------------- | ------- |
-| ``SECRET_ENCRYPTION_KEY`` | Secret used to encrypt secrets in database. | 64 character hex string | - |
+| Environment Variable | Description | Possible values | Default |
+| ----------------------- | ------------------------------------------- | ----------------------- | ------- |
+| `SECRET_ENCRYPTION_KEY` | Secret used to encrypt secrets in database. | 64 character hex string | - |
:::info
A random secret can be generated by using the following command: `openssl rand -hex 32`
@@ -53,27 +55,27 @@ A random secret can be generated by using the following command: `openssl rand -
## Docker
-| Environment Variable | Description | Possible values | Default |
-| ------------------------ | ----------- | --------------- | ------- |
-| ``DOCKER_HOSTNAMES`` | Comma-separated list of hostnames to connect to | For example `localhost,docker.example.com` | - |
-| ``DOCKER_PORTS`` | Comma-separated list of ports to connect to | For example `2375,2376` | - |
-| ``DOCKER_SOCKET_PATHS`` | Comma-separated list of mounted Docker-compatible socket paths. See the [Docker and Podman integration guide](/docs/integrations/docker) | For example `/var/run/docker.sock,/var/run/podman.sock` | - |
+| Environment Variable | Description | Possible values | Default |
+| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | ------- |
+| `DOCKER_HOSTNAMES` | Comma-separated list of hostnames to connect to | For example `localhost,docker.example.com` | - |
+| `DOCKER_PORTS` | Comma-separated list of ports to connect to | For example `2375,2376` | - |
+| `DOCKER_SOCKET_PATHS` | Comma-separated list of mounted Docker-compatible socket paths. See the [Docker and Podman integration guide](/docs/integrations/docker) | For example `/var/run/docker.sock,/var/run/podman.sock` | - |
## Database
-| Environment Variable | Description | Possible values | Default |
-| ------------------------ | ----------- | --------------- | ------- |
-| ``DB_DRIVER`` | Database driver to use. Currently `better-sqlite3` is used for sqlite, `mysql2` for mysql and `node-postgres` for postgresql | ``better-sqlite3`` / ``mysql2`` / ``node-postgres`` | ``better-sqlite3`` |
-| ``DB_DIALECT`` | Database dialect to use. | ``sqlite`` / ``mysql`` / ``postgresql`` | ``sqlite`` |
-| ``DB_URL`` | Database URL to connect to. | Any valid database URL | ``/appdata/db/db.sqlite`` |
-| ``DB_HOST`` | Database host to connect to. | Any valid database host | - |
-| ``DB_PORT`` | Database port to connect to. | Any valid database port | - |
-| ``DB_NAME`` | Database name to connect to. | Any valid database name | - |
-| ``DB_USER`` | Database user to connect with. | Any valid database user | - |
-| ``DB_PASSWORD`` | Database password to connect with. | Any valid database password | - |
+
+| Environment Variable | Description | Possible values | Default |
+| -------------------- | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | ----------------------- |
+| `DB_DRIVER` | Database driver to use. Currently `better-sqlite3` is used for sqlite, `mysql2` for mysql and `node-postgres` for postgresql | `better-sqlite3` / `mysql2` / `node-postgres` | `better-sqlite3` |
+| `DB_DIALECT` | Database dialect to use. | `sqlite` / `mysql` / `postgresql` | `sqlite` |
+| `DB_URL` | Database URL to connect to. | Any valid database URL | `/appdata/db/db.sqlite` |
+| `DB_HOST` | Database host to connect to. | Any valid database host | - |
+| `DB_PORT` | Database port to connect to. | Any valid database port | - |
+| `DB_NAME` | Database name to connect to. | Any valid database name | - |
+| `DB_USER` | Database user to connect with. | Any valid database user | - |
+| `DB_PASSWORD` | Database password to connect with. | Any valid database password | - |
You can either use the url or host, port, name and credentials combined. The URL will be prioritized over the other values.
-
## Redis
By default Redis is running within the installation. (for example in Docker).
@@ -83,25 +85,23 @@ However for example for K8s it can be useful to use an external Redis instance.
External redis is currently not supported with Proxmox Community Scripts
:::
-| Environment Variable | Description | Possible values | Default |
-| ------------------------ | ----------- | --------------- | ------- |
-| `REDIS_IS_EXTERNAL` | Whether Redis is running externally. | `true` / `false` | `false` |
-| `REDIS_HOST` | Hostname of the Redis instance. | Any valid hostname | - |
-| `REDIS_PORT` | Port of the Redis instance. | Any valid port | `6379` |
-| `REDIS_USERNAME` | Username to connect to the Redis instance. | Any valid username | - |
-| `REDIS_PASSWORD` | Password to connect to the Redis instance. | Any valid password | - |
-| `REDIS_DATABASE_INDEX` | Select which database of your redis instance should be used. | Any numeric index | - |
-| `REDIS_TLS_CA` | CA certificate for Redis TLS connections. If a certificate is specified the connection will use TLS. | Any valid CA certificate | - |
-
+| Environment Variable | Description | Possible values | Default |
+| ---------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------ | ------- |
+| `REDIS_IS_EXTERNAL` | Whether Redis is running externally. | `true` / `false` | `false` |
+| `REDIS_HOST` | Hostname of the Redis instance. | Any valid hostname | - |
+| `REDIS_PORT` | Port of the Redis instance. | Any valid port | `6379` |
+| `REDIS_USERNAME` | Username to connect to the Redis instance. | Any valid username | - |
+| `REDIS_PASSWORD` | Password to connect to the Redis instance. | Any valid password | - |
+| `REDIS_DATABASE_INDEX` | Select which database of your redis instance should be used. | Any numeric index | - |
+| `REDIS_TLS_CA` | CA certificate for Redis TLS connections. If a certificate is specified the connection will use TLS. | Any valid CA certificate | - |
## Proxy
-| Environment Variable | Description | Possible values | Default |
-| ------------------------ | ----------- | --------------- | ------- |
-| ``HTTP_PROXY`` | HTTP proxy to use | Any valid HTTP proxy URL | - |
-| ``HTTPS_PROXY`` | HTTPS proxy to use | Any valid HTTPS proxy URL | - |
-| ``NO_PROXY`` | Comma separated list of hosts that should be excluded from proxying | Any valid hostnames | - |
-
+| Environment Variable | Description | Possible values | Default |
+| -------------------- | ------------------------------------------------------------------- | ------------------------- | ------- |
+| `HTTP_PROXY` | HTTP proxy to use | Any valid HTTP proxy URL | - |
+| `HTTPS_PROXY` | HTTPS proxy to use | Any valid HTTPS proxy URL | - |
+| `NO_PROXY` | Comma separated list of hosts that should be excluded from proxying | Any valid hostnames | - |
## Advanced deployments
@@ -109,6 +109,12 @@ External redis is currently not supported with Proxmox Community Scripts
The advanced deployments environment variables should only be used if you know what you are doing.
:::
-| Environment Variable | Description |
-| ------------------------ | ----------- |
-| ``DB_MIGRATIONS_DISABLED`` | Disable db migrations. For example for helm charts |
+| Environment Variable | Description |
+| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
+| `DB_MIGRATIONS_DISABLED` | Disable db migrations. For example for helm charts. |
+| `WORKSHOP_PUBLIC_ORIGIN` | Origin used in PocketBase-generated links and email. It must be set explicitly in production. |
+| `PB_ALLOWED_ORIGINS` | Browser origins accepted by PocketBase. The central public community defaults to `*` so self-hosted Homarr instances can use OAuth. |
+
+All public URL variables reject non-HTTP protocols, embedded credentials, query strings, and fragments, and normalize
+trailing slashes when Homarr or Docusaurus starts. The Docusaurus-only `WORKSHOP_URL` name is accepted as a deprecated alias for
+`WORKSHOP_API_URL` for one release. `WORKSHOP_API_URL` takes precedence.
diff --git a/apps/docs/docs/advanced/icons/index.mdx b/apps/docs/docs/advanced/icons/index.mdx
index b73d49e02..4fd21d95f 100644
--- a/apps/docs/docs/advanced/icons/index.mdx
+++ b/apps/docs/docs/advanced/icons/index.mdx
@@ -9,14 +9,16 @@ tags:
---
## Icon picker
+
Icons in Homarr are automatically requested from multiple sources:
- - [Dashboard Icons](https://dashboardicons.com/) - Our recommended source with over 1888+ curated, high-quality icons
- - [selfh.st icons](https://github.com/selfhst/icons)
- - [Simple icons](https://github.com/simple-icons/simple-icons)
- - [Tabler icons](https://tabler-icons.io/)
- - [Papirus icons](https://github.com/PapirusDevelopmentTeam/papirus-icon-theme)
- - [Homelab Svg assets](https://github.com/loganmarchione/homelab-svg-assets)
- - **Local icons**: automatically fetched from the [medias feature](/docs/management/media/)
+
+- [Dashboard Icons](https://dashboardicons.com/) - Our recommended source with over 1888+ curated, high-quality icons
+- [selfh.st icons](https://github.com/selfhst/icons)
+- [Simple icons](https://github.com/simple-icons/simple-icons)
+- [Tabler icons](https://tabler-icons.io/)
+- [Papirus icons](https://github.com/PapirusDevelopmentTeam/papirus-icon-theme)
+- [Homelab Svg assets](https://github.com/loganmarchione/homelab-svg-assets)
+- **Local icons**: automatically fetched from the [medias feature](/docs/management/media/)
Using these icon sources, Homarr provides a total collection of over 11,000 icons at your fingertips.
The icon picker is a fast searcher that helps you to find your desired icon simply by entering a search term.
@@ -30,5 +32,6 @@ If you can't find the icon you're looking for in Homarr's built-in picker, we st
Icons that are high quality and printable ([SVG](https://wikipedia.org/wiki/Scalable_Vector_Graphics)) are marked with a red badge "SVG" at the top right.
### Using external icons
+
If you don't find the icon you're looking for on Dashboard Icons or wish to use a different source, you can simply enter a URL that points to an icon.
Direct HTTP(S) image URLs are used as entered instead of being searched in the built-in repositories. It is important that the URL directly points to the file without any advertisements or elements around it.
diff --git a/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx b/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx
index 8dfe0f285..bc971bc4a 100644
--- a/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx
+++ b/apps/docs/docs/advanced/keyboard-shortcuts/index.mdx
@@ -6,16 +6,22 @@ tags:
Homarr has the following keyboard shortcuts:
-| Shortcut | Description |
-| -------- | ----------- |
-| `MOD` + `K` | Opens the search bar |
-| `MOD` + `J` | Toggles Dark Mode |
-| `MOD` + `E` | Toggles Edit Mode |
+| Shortcut | Description |
+| ---------------------------------------------- | ------------------------------------------ |
+| `MOD` + `K` | Opens the search bar |
+| `MOD` + `J` | Toggles Dark Mode |
+| `MOD` + `E` | Toggles Edit Mode |
+| Hold `Shift` while hovering a supported widget | Opens its temporary advanced view |
+| `Shift` + `Enter` on a supported widget | Opens its advanced view until it is closed |
+| `Esc` | Closes an open advanced view or board menu |
:::tip
Here `MOD` refers to the modifier key, which is `Ctrl` on Windows and `Cmd` on macOS.
+Advanced view is available outside edit mode. Widgets without a distinct advanced experience adapt to their normal
+dimensions instead.
+
:::
:::note
diff --git a/apps/docs/docs/advanced/proxy/index.mdx b/apps/docs/docs/advanced/proxy/index.mdx
index ef4e41f1d..fbe33bfa4 100644
--- a/apps/docs/docs/advanced/proxy/index.mdx
+++ b/apps/docs/docs/advanced/proxy/index.mdx
@@ -9,16 +9,17 @@ tags:
- Security
---
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
## Allowing self-signed certificates
+
Some users may come across a barrier, where they're unable to receive a 200 response from the Ping widget for some apps, while using self-signed certificates or a local certificate authory.
:::note What's going on?
Homarr is trying to communicate to your apps via the integrations.
-It usually doesn't matter if Homarr is running on ``http`` or ``https``.
+It usually doesn't matter if Homarr is running on `http` or `https`.
Your apps have a self-signed certificate - Homarr will recognize that the certificate was signed by an unknown authority and requests will be blocked.
:::
@@ -28,8 +29,9 @@ To allow self-signed certificates, you can configure them on the certificates ma
## Securing Homarr with Traefik
Copying the configuration straight from the docker-compose file won't work if you are running Homarr behind Traefik, such as a Portainer setup, or docker-swarm. In that case, you should use the following slightly modified configuration:
+
```yaml
-version: '3'
+version: "3"
services:
homarr:
container_name: homarr
@@ -47,18 +49,18 @@ services:
traefik.http.routers.homarr.rule: Host(`your.internal.dns.address.here.com`)
traefik.http.routers.homarr.entrypoints: websecure
traefik.http.routers.homarr-secure.app: homarr
- traefik.http.apps.homarr.loadbalancer.server.port: 7575
networks:
proxy:
external: true
```
-
+
A sample Traefik docker-compose.yml using Cloudflare for certificate generation that works with the configuration above would be:
+
```yaml
-version: '3'
+version: "3"
apps:
traefik:
@@ -104,7 +106,6 @@ networks:
external: true
```
-
:::tip
Of particular note here is that both configurations explicitly define which network they are using, in this case "proxy", but it can be named anything. It just has to be the same across all apps for which Traefik is serving as a proxy. These are marked as external because the proxy network was manually created by running: `docker network create proxy` but this might be unnecessary depending on HOW exactly you are running Traefik. For example, if running [Traefik with Portainer](https://docs.portainer.io/advanced/reverse-proxy/traefik#deploying-in-a-docker-standalone-scenario), you can follow their official docs on how to set up Traefik and Portainer together, and you can just focus on the Homarr docker labels instead.
:::
@@ -115,7 +116,6 @@ If you are using Caddy as the reverse proxy for your setup, your `docker-compose
```yaml
services:
-
# <-- [Homarr installation]
caddy:
@@ -141,14 +141,17 @@ networks:
proxy:
external: true
```
+
:::important
Homarr needs to be on the same network so you will need to add the following property to your homarr install:
+
```yaml
homarr:
...
networks:
- proxy
```
+
:::
Next, create a file named `Caddyfile` at the same level as your `docker-compose.yml` file with the following content:
diff --git a/apps/docs/docs/advanced/running-as-different-user/index.mdx b/apps/docs/docs/advanced/running-as-different-user/index.mdx
index a4ebcf0f4..91c02fb9d 100644
--- a/apps/docs/docs/advanced/running-as-different-user/index.mdx
+++ b/apps/docs/docs/advanced/running-as-different-user/index.mdx
@@ -8,7 +8,7 @@ tags:
- PUID
---
-By default the container is running with user `root` and group `root`.
+By default the container is running with user `root` and group `root`.
You can change the user and group by using the `PUID` and `PGID` environment variables.
Important to note is, that you'll also need to change the permissions for the mounted directories accordingly.
@@ -32,11 +32,11 @@ services:
volumes:
- ./homarr/appdata:/appdata
environment:
- - SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
- - PUID=1000
- - PGID=1000
+ - SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
+ - PUID=1000
+ - PGID=1000
ports:
- - '7575:7575'
+ - "7575:7575"
```
After that you'll need to create the appdata directory and set the correct permissions:
@@ -76,11 +76,11 @@ services:
- /var/run/docker.sock:/var/run/docker.sock
- ./homarr/appdata:/appdata
environment:
- - SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
- - PUID=1000
- - PGID=999
+ - SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
+ - PUID=1000
+ - PGID=999
ports:
- - '7575:7575'
+ - "7575:7575"
```
And set the permissions for the mounted directories:
@@ -90,4 +90,4 @@ mkdir -p ./homarr/appdata
chown -R 1000:999 ./homarr/appdata
```
-Important here is that the group ID is set to `999` as we created the `docker` group with this ID.
\ No newline at end of file
+Important here is that the group ID is set to `999` as we created the `docker` group with this ID.
diff --git a/apps/docs/docs/advanced/single-sign-on/index.mdx b/apps/docs/docs/advanced/single-sign-on/index.mdx
index ae1d9ca31..505fe9867 100644
--- a/apps/docs/docs/advanced/single-sign-on/index.mdx
+++ b/apps/docs/docs/advanced/single-sign-on/index.mdx
@@ -10,20 +10,21 @@ tags:
- Active Directory
---
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
# Single Sign On
Homarr supports multiple authentication options, from internal userbase (credentials), to LDAP (with Active directory support), and OIDC.
### Auth configuration common variables
-| Environment Variable | Description | Default Value |
-|---------------------------|-------------|---------------|
-| AUTH_PROVIDERS | Select Which provider to use between *credentials*, *ldap* and *oidc*. Multiple providers can be enabled with by separating them with `,`, (ex. `AUTH_PROVIDERS=credentials,oidc`, it is highly recommended to just enable one provider). | credentials |
-| AUTH_LOGOUT_REDIRECT_URL | URL to redirect to after clicking logging out. | --- |
-| AUTH_SESSION_EXPIRY_TIME | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") | "30d" |
-| AUTH_COOKIE_PREFIX | Prefix used for all authentication cookies (session, csrf, callback, pkce, state, nonce). Change this if you run another Auth.js/NextAuth app on the same hostname as Homarr (browsers scope cookies by host, not by port) to avoid cookie name collisions. Only letters, numbers, hyphens and underscores are allowed. | `homarr` |
+
+| Environment Variable | Description | Default Value |
+| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
+| AUTH_PROVIDERS | Select Which provider to use between _credentials_, _ldap_ and _oidc_. Multiple providers can be enabled with by separating them with `,`, (ex. `AUTH_PROVIDERS=credentials,oidc`, it is highly recommended to just enable one provider). | credentials |
+| AUTH_LOGOUT_REDIRECT_URL | URL to redirect to after clicking logging out. | --- |
+| AUTH_SESSION_EXPIRY_TIME | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") | "30d" |
+| AUTH_COOKIE_PREFIX | Prefix used for all authentication cookies (session, csrf, callback, pkce, state, nonce). Change this if you run another Auth.js/NextAuth app on the same hostname as Homarr (browsers scope cookies by host, not by port) to avoid cookie name collisions. Only letters, numbers, hyphens and underscores are allowed. | `homarr` |
diff --git a/apps/docs/docs/advanced/styling/index.mdx b/apps/docs/docs/advanced/styling/index.mdx
index 9be382ec3..56d4305e5 100644
--- a/apps/docs/docs/advanced/styling/index.mdx
+++ b/apps/docs/docs/advanced/styling/index.mdx
@@ -19,12 +19,14 @@ Homarr uses https://mantine.dev/ under the hood. You can change the look & feel
using custom CSS, that extends or overwrites the styles from Mantine.
## Getting into CSS
+
If you don't have any experience with CSS,
we strongly recommend you to go through the courses here: https://www.w3schools.com/css/.
Due to the infinite scenarios and setups possible with custom CSS,
our support team will not provide any support with your own written CSS.
## Limitations and dangers
+
When using custom CSS, you can break the page or certain functionalities.
Although CSS can improve the design & feel of the page, it **cannot do any of the following**:
@@ -33,21 +35,23 @@ Although CSS can improve the design & feel of the page, it **cannot do any
- Completely revamp the design of the page, only smaller changes are possible
Here are some dangers of using custom CSS:
+
- Updates may contain breaking changes in the structure, built-in styling or how styling is processed. Therefore,
-custom CSS may break unexpectedly without any warning
+ custom CSS may break unexpectedly without any warning
- Custom CSS may make certain elements invisible or not possible to click / interact with
- Custom CSS could make readability and accessibility worse for impaired people
## How to inspect the page
-When styling the page, you can use the ``Inspector`` to inspect elements on the page and try out styles.
+
+When styling the page, you can use the `Inspector` to inspect elements on the page and try out styles.
This may vary depending on your browser and operating system, but the workflow usually goes like this:
- Open the Homarr page
-- Press ``Ctrl`` + ``Shift`` + ``I`` at the same time.
-Alternatively, right-click on the page and choose ``Inspect`` or ``Inspect element``.
-- Open the tab ``Elements`` if it isn't open yet.
+- Press `Ctrl` + `Shift` + `I` at the same time.
+ Alternatively, right-click on the page and choose `Inspect` or `Inspect element`.
+- Open the tab `Elements` if it isn't open yet.
- Search the element you want to change in the DOM tree or use the element picker (usually at the top left)
-to select it by clicking on the page.
+ to select it by clicking on the page.
- Experiment by changing the CSS code on the right side
When writing custom CSS, it is important to also remember or create a selector, which tells the browser
@@ -55,17 +59,19 @@ which element to apply the styles to. If you accidentally broke something or wan
simply reload the page.
## Using Homarr's classes
+
Homarr's elements will already have a lot of classes and styles pre-applied.
You can reuse those classes to apply additional styles or overwrite them.
-When overwriting, remember to add the ``!important`` flag to your CSS property to tell the browser to use our instruction instead.
+When overwriting, remember to add the `!important` flag to your CSS property to tell the browser to use our instruction instead.
-Classes with random number suffixes, such as ``mantine-Card-x2fske``, should be avoided, since those are dynamic
+Classes with random number suffixes, such as `mantine-Card-x2fske`, should be avoided, since those are dynamic
CSS classes that will change with every build. This means, every time you update Homarr,
your styles will no longer apply to them
## Defining custom classes for widgets
+
If you want to apply the styles only for one widget, you can define custom class names only for that widget.
-To do that, enter edit mode on your board, click the three dots of your widget, click ``Edit item`` and click ``Advanced options``.
+To do that, enter edit mode on your board, click the three dots of your widget, click `Edit item` and click `Advanced options`.
Then, you can enter as many custom classes as you'd like:

@@ -73,6 +79,7 @@ Then, you can enter as many custom classes as you'd like:
To save them, click `Save changes` on both modals and exit edit mode.
## Where to enter custom CSS
+
After you wrote your custom CSS, it is time to permanently save it, so you don't need to use the inspector anymore.
To do this, enter the board settings:
@@ -84,11 +91,12 @@ Here you can enter your CSS code, which will be applied on the respective board

## Using variables
+
You can also change the values of the theme variables, which are located here: https://mantine.dev/styles/css-variables-list/.
To do this, see the following example CSS:
```css
:root {
- --mantine-primary-color-filled: #ffbb00;
+ --mantine-primary-color-filled: #ffbb00;
}
-```
\ No newline at end of file
+```
diff --git a/apps/docs/docs/community/donate.mdx b/apps/docs/docs/community/donate.mdx
index aeb95dcac..3651a2a7b 100644
--- a/apps/docs/docs/community/donate.mdx
+++ b/apps/docs/docs/community/donate.mdx
@@ -7,12 +7,15 @@ tags:
---
# Donate
+
## Why to donate
+
Our team of 4 developers is working on Homarr in their free time.
Homarr has become a big part of our daily things we work on - and therefore we invest a lot of time into maintaining Homarr,
providing support and implementing new features.
We also incur monetary costs while developing Homarr for the tools that we need.
A few examples include:
+
- Renting for a Cloud VPS for our cloud services (status page, monitoring, email, analytics, [demo instance](https://demo.homarr.dev/), ...)
- Paying for (IDE-) licenses
@@ -20,6 +23,7 @@ Using a donation, you help us to cover these expenses and enable the development
Homarr is completely free to use. We'd highly appreciate if you consider donating, especially if you use Homarr in a commercial environment.
## Where to donate
+
We accept donations on our official [OpenCollective](https://opencollective.com/homarr).
You may also sponsor us directly by providing a useful service / product to us other means.
-You can get into contact with us [here](/docs/community/get-in-touch).
\ No newline at end of file
+You can get into contact with us [here](/docs/community/get-in-touch).
diff --git a/apps/docs/docs/community/faq.mdx b/apps/docs/docs/community/faq.mdx
index 465c7bbaf..19f2cd0e0 100644
--- a/apps/docs/docs/community/faq.mdx
+++ b/apps/docs/docs/community/faq.mdx
@@ -5,43 +5,57 @@ 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 installation guide 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 here.
### Can I use my own custom icons for apps?
+
Yes, check out our guide on how to add your custom icons.
### 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...).
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. A [Custom Widget](/docs/management/custom-widgets) can read a public or private HTTP API.
-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.
+It can show responsive Mantine JSX. It can also use widget options and actions with board permission checks.
+
+Custom JSX v2 is the only Custom Widget format. Write the JSON and JSX in the workbench.
+
+You can also use the AI copy prompt. An MCP agent can validate and preview a definition before it saves the definition.
+
+The workbench reads a pasted AI response. It shows request capabilities and redacted diagnostics.
+
+A template cannot import packages or run arbitrary JavaScript. It cannot call browser `fetch`.
+
+Named requests use the Homarr server. The server applies board permissions and the network policy.
+
+Before installation, examine the origins, methods, permissions, and actions. Use the same review before publication.
### 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.
@@ -49,39 +63,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**
-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/.
### 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 here.
### 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 GluetunVPN 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 here to get with us in touch.
-
+
:::tip
diff --git a/apps/docs/docs/community/get-in-touch.mdx b/apps/docs/docs/community/get-in-touch.mdx
index 7651f5ea2..eecd2e1f3 100644
--- a/apps/docs/docs/community/get-in-touch.mdx
+++ b/apps/docs/docs/community/get-in-touch.mdx
@@ -9,16 +9,16 @@ tags:
We're happy to talk with you! Shoot us a message at the following places:
## E-Mail
+
We are open to discuss sponsorships, security vulnerabilities and other private requests through our email.
Please do not report any bugs / feature requests here - they will be ignored and deleted.
[homarr-labs@proton.me](mailto:homarr-labs@proton.me)
## Discord
+
We have a Discord Server, where we announce new releases, discuss features and bugs and show off our dashboards.
There are over 4'000 members and we're happy to answer your questions or concerns.
You're also open to discuss new / upcoming features and any offtopic selfhosting content.
[__](https://discord.gg/aCsmEV5RgA)
-
-
diff --git a/apps/docs/docs/community/license.mdx b/apps/docs/docs/community/license.mdx
index e36a5d684..3d1f82890 100644
--- a/apps/docs/docs/community/license.mdx
+++ b/apps/docs/docs/community/license.mdx
@@ -7,6 +7,7 @@ tags:
Homarr is licensed under [Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0).
## TL;DR
+
| Action | Allowed |
| -------------- | ------- |
| Copy | ✅ |
@@ -24,8 +25,6 @@ Please consider [making a donation](/docs/community/donate) to Homarr, if this p
:::
-
-
## Full License
```txt
diff --git a/apps/docs/docs/community/translations.mdx b/apps/docs/docs/community/translations.mdx
index 074db939d..68eea8a1e 100644
--- a/apps/docs/docs/community/translations.mdx
+++ b/apps/docs/docs/community/translations.mdx
@@ -5,30 +5,31 @@ tags:
- Translations
---
-We want to make Homarr accessible for everyone.
-But to do that, we need your help.
-We are running a community program that enables everyone to translate Homarr into their language – no programming required.
-We are using [Crowdin](https://crowdin.com/) to manage our translations.
+We want to make Homarr accessible for everyone.
+But to do that, we need your help.
+We are running a community program that enables everyone to translate Homarr into their language – no programming required.
+We are using [Crowdin](https://crowdin.com/) to manage our translations.
You can find the translations [here](https://translate.homarr.dev/).
## How to translate
-
### Using crowdin's online platform
-Go to the [Crowdin translations](https://translate.homarr.dev/) and select a language you want to translate.
-You can then enter the translation mode and start translating.
-After you've submitted your translations, a Proofreader will review your changes.
-After a translation has been approved, it will be merged to the language development branch.
-We merge this one periodically to the development branch.
+
+Go to the [Crowdin translations](https://translate.homarr.dev/) and select a language you want to translate.
+You can then enter the translation mode and start translating.
+After you've submitted your translations, a Proofreader will review your changes.
+After a translation has been approved, it will be merged to the language development branch.
+We merge this one periodically to the development branch.
After the next update of Homarr, all changes from the development branch will be merged, including the changes in the translations.
### Using live translate
+
You can also help translate homarr directly from the app!
For this you can simply change your language to "Crowdin (live translations)"

-Then you'll be prompted to connect to your Crowdin account and choose the language in which you want to transalte.
+Then you'll be prompted to connect to your Crowdin account and choose the language in which you want to transalte.
You can then start translating directly from the app by selecting some text on the app.
If some text cannot be selected to be edited with the live translation tool, you can still translate it inside of the In-Context popup at the Home tab, it will be red if it's not translated yet.
@@ -47,7 +48,8 @@ Do you want to become a proofreader? [Let us know on our Discord](/docs/communit
:::
## Guidelines
+
1. Translate with context: Some translations can't be translated 1:1 - they need context. Ensure that your translations make sense in the context.
2. Keep things simple and original - no additional characters are needed.
3. Don't translate the same thing twice - if you see a translation that is already translated, don't translate it again unless you have a better translation.
-5. Keep your translations as close to the original as possible.
+4. Keep your translations as close to the original as possible.
diff --git a/apps/docs/docs/getting-started/after-the-installation.mdx b/apps/docs/docs/getting-started/after-the-installation.mdx
index 7ac904bf4..3785aac56 100644
--- a/apps/docs/docs/getting-started/after-the-installation.mdx
+++ b/apps/docs/docs/getting-started/after-the-installation.mdx
@@ -54,11 +54,7 @@ You must choose a username and password (both can later be changed).
We enforce strict password requirements when you enter your password to protect your instance if you accidentally
expose it to the internet.
-
+
If you selected external providers (such as LDAP or OIDC), you'll be prompted to create an admin group.
This group will be used to synchronize users from your provider to Homarr.
@@ -116,22 +112,14 @@ The onboarding can no longer be started again.
From here you can click on one of the provided links to continue your journey.
We recommend you to continue with the section [creating your first board on this page](#creating-your-first-board).
-
+
### Importing a ZIP from version before 1.0.0
This is recommended when you used Homarr before and want to re-import your data.
It is important that you first migrate correctly using [the list of breaking changes for 1.0](/blog/2024/09/23/version-1.0).
-
+
**How to export and import data**
@@ -149,7 +137,8 @@ It is important that you first migrate correctly using [the list of breaking cha
## Creating your first board
In Homarr, you organize your apps and widgets on boards.
-Boards can be organized using the interactive drag and drop.
+Boards use fixed-size tiles so the same widget keeps the same proportions on every screen.
+Drag, resize, and section controls are available in edit mode.
Let's create your first board by navigating here: `Manage` > `Boards` > `New board`:

@@ -197,38 +186,28 @@ Here you have the following options in the header:
When entering the edit mode, you'll get some additional buttons to work with:
-
+
- **Add item**: Click this button to add new elements to your page.
-- **Exit edit mode**: Click this btuton to save your changes and exit edit mode.
+- **Exit edit mode**: Click this button to save your changes and exit edit mode.
+
+Outside edit mode, right-click a widget for its live status, refresh, settings, quick options, and supported actions.
+Hold `Shift` while hovering a widget that supports advanced view to expand it in place while the rest of the board
+stays visible but dimmed. Release `Shift` to close it. You can also focus that widget and press `Shift` + `Enter` to
+keep its advanced view open until you close it. Widgets without a distinct advanced experience adapt in place.
To continue, click the option "New Item":
-
+
Next, you can choose what item you'd like to add.
We will discuss the other options in the following sections - for now choose "App" to continue.
-
+
As soon as you chose the app, the modal will close and you'll get the following item on your board:
-
+
This is expected, because you haven't told Homarr what app it should show in this item.
To configure this, click on the small 3 dots at the top of the item in the edit mode and choose "Edit item" in the menu.
@@ -240,21 +219,28 @@ In the modal you can edit the app that you want to display:

As soon as you save your changes, the tile on the board will refresh and display your app.
-Using the small bidirectional arrow at the bottom right you can resize the element.
-Start pressing with your mouse button down, move your mouse to resize and release your finger as soon your you're happy with the size.
+Use the resize handle at the bottom right to resize the tile.
+Press and hold the handle, move the pointer, and release it when the tile has the size you want.

-To move your item to a different position on the board, simply hold the mouse button anywhere on the item and move with the mouse to the desired position.
-As soon as you release your finger, the item will snap in place in the grid.
+To move a tile in edit mode, press and hold its content, move it to the desired position, and release it.
+Widget content is disabled while editing so its controls do not intercept the drag; use the tile's settings button for edit actions.
+The tile snaps to the grid. Equally-sized tiles swap when one is dropped into the other's slot, while other neighboring tiles move out of the way automatically.

+Every `1 × 1` tile has a logical content size of `200 × 200` pixels.
+Larger tiles use multiples of that fixed unit, with a consistent gap between cells.
+Homarr scales the complete board uniformly, like zooming a page.
+It stops shrinking at 75% and lets you scroll horizontally instead of making text and controls too small.
+It does not squeeze or reflow the content of individual widgets, so a widget keeps the same design and proportions across screen sizes.
+
:::note
-You cannot move items freely on the vertical axis.
-We always align all items to the top of the page to simply organization.
-To move items to the bottom, you must add more items or resize items.
+Viewing a board initially uses the lightweight, non-interactive layout.
+For users with edit access, Homarr prepares drag-and-drop and resize controls in the background after the board becomes interactive.
+If you enter edit mode before that finishes, the existing board stays visible until the editor is ready.
:::
@@ -265,8 +251,10 @@ This helps you to explore the capabilities of Homarr before you build your board
:::
-Users on touch or mobile devices may not be able to drag and drop or move items.
-They can use the "Move / resize item" option in the edit menu of items instead:
+Keyboard users can focus a tile, press Enter or Space, then use arrow keys to move and Shift plus arrow keys to resize.
+Press Escape to stop.
+For precise numeric controls or touch input, choose **Move / resize item** from the tile's edit menu.
+The dialog can also move the tile to another section or fixed sidebar:

@@ -338,43 +326,36 @@ As with every change, don't forget to save your changes using the exit edit mode

-## Grouping your items
+## Organizing items with Containers
-As the last step, let's organize your freshly created items using two different options.
-You can choose either or both of them on your boards.
+Containers are resizable areas for apps, widgets, and nested Containers.
+The same Container works on the main canvas, inside a fixed rail, or inside another Container.
+Each Container can configure:
-### Using dynamic sections
+- Its label and whether that label is visible.
+- Allow or disable collapsing.
+- Show or hide **Open all in new tabs**.
+- Change its border color and custom CSS classes.
-Dynamic sections are a simple, resizable visual group to group items.
+Open the add-content menu and choose **New Container**.
+In edit mode, move and resize it like any other item, then drag items into or out of it.
+Nested content stays within the Container's bounds.
+While resizing, the solid outline follows the pointer continuously and the dashed grid preview shows the size that will be saved on release.
-
+### Collapsing and opening apps
-After adding a dynamic section, you can simply drag and drop items into the section:
+Users can collapse an eligible Container like an accordion in view mode without changing its saved expanded size.
+The full-width header becomes the compact expansion bar, so the half-row state has no empty pill-shaped spacer.
+Expanding it restores its bounds and makes room for its contents.
+If **Open all in new tabs** is enabled, the Container menu can open every app link it contains.
-
+### Fixed rails
-### Using categories
-
-Categories are more advanced and complicated than dynamic sections.
-To create a category, open the add item modal and choose "new category":
-
-
-
-Every category must have a name. After you click "Add category", you will get a new item on your board:
-
-
-
-You can drag and drop items in to this category in the edit mode.
-Categories will have a big space before and after them in the edit mode to
-enable you to drag and drop items outside the category.
-You can also collapse or open categories in edit mode to facilitate your editing process.
-
-:::note
-
-A current limitation of categories is, that they will always have a width of 100%.
-You cannot reduce their horizontal width.
-
-:::
+Open the board's **Layout** settings to enable the left or right rail for each responsive layout.
+Choose one, two, or three columns with the slider and use the preview to check the remaining canvas width.
+The rail reserves those columns from the dashboard, so Homarr moves canvas items into the next available positions.
+Rail content stays visible while the page scrolls and remains editable.
+Each responsive layout keeps its own rail widths, item positions, and item sizes.
## Guided tours
diff --git a/apps/docs/docs/getting-started/glossary.mdx b/apps/docs/docs/getting-started/glossary.mdx
index 8d690245c..989c71e1c 100644
--- a/apps/docs/docs/getting-started/glossary.mdx
+++ b/apps/docs/docs/getting-started/glossary.mdx
@@ -1,12 +1,13 @@
# Glossary
-In the glossary, you'll learn the different components and their names of Homarr.
-| Term | Definition |
-|-----------------|----------------------------------------------------------------------------------------------------------------|
-| Apps | An piece of software that you or a third party hosts (eg. Google) |
-| Integration | A connection to a service outside of Homarr |
-| Dynamic section | Resizeable area where items on your board can be placed and grouped |
-| Widget | A tile that uses your integrations to display data or enables you to interact with with them |
-| Edit mode | Temporary state that you can enter where you can drag and drop, edit or customize your board |
-| Board | Customizable page which can be edited using the edit mode. This is where apps, sections and widgets are placed |
-| Tasks | Workloads that are performed in the background - even if there is no user present |
+This glossary explains Homarr's main components and the terms used for them.
+
+| Term | Definition |
+| ----------- | --------------------------------------------------------------------------------------- |
+| Apps | A piece of software that you or a third party hosts (for example, Google) |
+| Integration | A connection to a service outside of Homarr |
+| Container | A resizable area that can hold apps, widgets, and nested Containers |
+| Widget | A tile that uses your integrations to display data or enables you to interact with them |
+| Edit mode | A temporary state where you can drag, resize, edit, or customize your board |
+| Board | A customizable page where apps, widgets, and Containers are arranged |
+| Tasks | Workloads that are performed in the background, even if no user is present |
diff --git a/apps/docs/docs/getting-started/index.mdx b/apps/docs/docs/getting-started/index.mdx
index 4dc986658..0d6a44a4e 100644
--- a/apps/docs/docs/getting-started/index.mdx
+++ b/apps/docs/docs/getting-started/index.mdx
@@ -8,13 +8,14 @@ tags:
- Unraid
---
-import Link from '@docusaurus/Link';
-import { IconArrowRight } from '@tabler/icons-react';
+import Link from "@docusaurus/Link";
+import { IconArrowRight } from "@tabler/icons-react";
Before you try to install Homarr, please review if your device meets the requirements.
Installing on an unsupported device may lead to unstable behaviour and no support will be provided.
## Hardware
+
- At least 500MB RAM
- At least 600MB of free disk space for the Docker image. SSD is preferred.
- Single or dual core CPU
@@ -24,8 +25,8 @@ Installing on an unsupported device may lead to unstable behaviour and no suppor
We build Homarr for a bunch of different architectures, including ARM. Please ensure that your device architecture is supported by Homarr. We won't be able to provide support for unusual or outdated architectures.
-| Architecture | Supported |
-|--------------|-----------------|
+| Architecture | Supported |
+| ------------ | ---------------- |
| linux/amd64 | ✅ Supported |
| linux/arm64 | ✅ Supported |
| linux/arm/v7 | ❌ Not supported |
@@ -34,10 +35,10 @@ We build Homarr for a bunch of different architectures, including ARM. Please en
-
+
Go to Installation
diff --git a/apps/docs/docs/getting-started/installation/docker.mdx b/apps/docs/docs/getting-started/installation/docker.mdx
index 956a6c2d6..5a56a848a 100644
--- a/apps/docs/docs/getting-started/installation/docker.mdx
+++ b/apps/docs/docs/getting-started/installation/docker.mdx
@@ -1,21 +1,23 @@
-import { DockerInstallSnippet } from '@site/src/components/pages/installation/docker/dynamic-docker-install'
+import { DockerInstallSnippet } from "@site/src/components/pages/installation/docker/dynamic-docker-install";
# Docker
+
Docker is our recommended installation method for beginners and professionals.
It is easy to use, ships with all dependencies inside a single container and is easy to use and maintain.
It requires at least some basic knowledge of [Docker](https://www.docker.com/).
We recommend [NetworkChuck's Docker Containers 101](https://www.youtube.com/watch?v=eGz9DS-aIeY) as a good start for complete beginners.
## Docker compose
+
### Prerequisites
- A Linux or Windows based system that meets the [hardware prerequisites](/docs/getting-started/#hardware) and [architecture prerequisites](/docs/getting-started/#architecture).
- [Docker](https://docs.docker.com/get-docker/)
-- [Docker Compose](https://docs.docker.com/compose/install/) - *preinstalled on many systems nowadays*
+- [Docker Compose](https://docs.docker.com/compose/install/) - _preinstalled on many systems nowadays_
### Installation
-
+
Finally, run ``docker compose up -d`` in the same directory. This will start the Homarr container in the background.
:::caution
@@ -26,27 +28,26 @@ Docker compose creates a network by default. Some integrations may not work (spe
### Updating
-To update, navigate to the directory with the ``docker-compose.yaml`` located.
+To update, navigate to the directory with the `docker-compose.yaml` located.
-- Stop Homarr using ``docker compose down``
-- Pull the newest image of Homarr using ``docker compose pull``
-- Start Homarr again using ``docker compose up -d`` (``-d`` for detached mode - start in background)
-- Delete the old image using ``docker image prune`` (*Warning: this also removes you other unused images - not just Homarr*)
+- Stop Homarr using `docker compose down`
+- Pull the newest image of Homarr using `docker compose pull`
+- Start Homarr again using `docker compose up -d` (`-d` for detached mode - start in background)
+- Delete the old image using `docker image prune` (_Warning: this also removes you other unused images - not just Homarr_)
You can automate this process using [Watchtower](https://containrrr.dev/watchtower/).
### Uninstalling
-To uninstall, navigate to the directory with the ``docker-compose.yaml`` located.
+To uninstall, navigate to the directory with the `docker-compose.yaml` located.
-- Stop Homarr using ``docker compose down``
-- Delete the created directories & files on your root file system (check ``docker-compose.yaml`` file for your specific locations)
-- Delete compose file using ``rm docker-compose.yaml``
-- Prune unused Docker images using ``docker image prune`` (*Warning: this also removes you other unused images - not just Homarr*)
+- Stop Homarr using `docker compose down`
+- Delete the created directories & files on your root file system (check `docker-compose.yaml` file for your specific locations)
+- Delete compose file using `rm docker-compose.yaml`
+- Prune unused Docker images using `docker image prune` (_Warning: this also removes you other unused images - not just Homarr_)
- (Optional): Prune any network or volumes if you use any.
- (Optional): Remove Homarr from your reverse proxy, VPNs or tunnels if you use any.
-
## Docker standalone
:::caution
@@ -77,15 +78,16 @@ docker run \
:::note
-Docker will mount the configuration files and icons to your host machine. Please make sure to replace ```` from the docker run command with your desired storage location. The path must be absolute.
+Docker will mount the configuration files and icons to your host machine. Please make sure to replace `` from the docker run command with your desired storage location. The path must be absolute.
:::
### Updating
+
To update Homarr, you must remove your container first. Make sure that you've mounted your data and that you have access to it, so your configuration doesn't get lost.
-1. Run ``docker rm homarr`` to remove the container.
-2. Pull the latest Homarr image ``docker pull ghcr.io/homarr-labs/homarr:latest``.
+1. Run `docker rm homarr` to remove the container.
+2. Pull the latest Homarr image `docker pull ghcr.io/homarr-labs/homarr:latest`.
3. Re-run the command you used to install Homarr.
This process can get tideous, if you update frequently. Thus, we recommend [using docker-compose](#docker-compose)
@@ -98,8 +100,8 @@ Want to update all your containers automatically? Checkout [Watchtower](https://
### Uninstalling
-1. Obtain the container ID of your Homarr container using ``docker ps | grep homarr``.
-2. Delete the container using ``docker rm ``
-3. Prune unused Docker images using ``docker image prune`` (*Warning: this also removes you other unused images - not just Homarr*)
+1. Obtain the container ID of your Homarr container using `docker ps | grep homarr`.
+2. Delete the container using `docker rm `
+3. Prune unused Docker images using `docker image prune` (_Warning: this also removes you other unused images - not just Homarr_)
4. (Optional): Prune any network or volumes if you use any.
5. (Optional): Remove Homarr from your reverse proxy, VPNs or tunnels if you use any.
diff --git a/apps/docs/docs/getting-started/installation/helm.md b/apps/docs/docs/getting-started/installation/helm.md
index 051c38955..7507cdc18 100644
--- a/apps/docs/docs/getting-started/installation/helm.md
+++ b/apps/docs/docs/getting-started/installation/helm.md
@@ -2,9 +2,9 @@
-
+

-
+
A Helm chart to deploy homarr for Kubernetes
@@ -12,7 +12,7 @@ A Helm chart to deploy homarr for Kubernetes
## Source Code
-*
+-
## Requirements
@@ -76,38 +76,38 @@ To avoid including sensitive information in plain text within your version contr
Below is an exhaustive list of all secrets:
-| FEATURE | SECRET NAME | SECRET KEYS | Required |
-|-----------|-------------------------|-----------------------------------------|----------|
-| OIDC | auth-oidc-secret | oidc-client-id oidc-client-secret | No |
-| LDAP | auth-ldap-secret | bind-password | No |
-| DATABASE | db-secret | db-url | No |
-| DATABASE | db-encryption | db-encryption-key | yes |
+| FEATURE | SECRET NAME | SECRET KEYS | Required |
+| -------- | ---------------- | ------------------------------------ | -------- |
+| OIDC | auth-oidc-secret | oidc-client-id oidc-client-secret | No |
+| LDAP | auth-ldap-secret | bind-password | No |
+| DATABASE | db-secret | db-url | No |
+| DATABASE | db-encryption | db-encryption-key | yes |
### Database
You have multiple options for configuring the database:
-| DRIVER TYPE | Persistence mode |
-|----------------|---------------------------------|
-| better-sqlite3 | Pod disk |
-| better-sqlite3 | homarr-database PVC |
-| mysql2 | External MySql database |
-| node-postgres | External Postgresql database |
+| DRIVER TYPE | Persistence mode |
+| -------------- | ---------------------------- |
+| better-sqlite3 | Pod disk |
+| better-sqlite3 | homarr-database PVC |
+| mysql2 | External MySql database |
+| node-postgres | External Postgresql database |
#### Pod disk
-No additional configuration is required. However, keep in mind that if the pod restarts, all data will be lost. This setup is not *recommended* for production use.
+No additional configuration is required. However, keep in mind that if the pod restarts, all data will be lost. This setup is not _recommended_ for production use.
To create the necessary database secret, execute the following command:
Required Secrets
-````yaml
+```yaml
kubectl create secret generic db-encryption \
--from-literal=db-encryption-key='' \
--namespace homarr
-````
+```
@@ -120,11 +120,11 @@ Associated secret to create :
DB Required Secrets
-````yaml
+```yaml
kubectl create secret generic db-encryption \
--from-literal=db-encryption-key='' \
--namespace homarr
-````
+```
@@ -133,13 +133,13 @@ Bellow an example of the override value file :
values.yaml
-````yaml
+```yaml
persistence:
homarrDatabase:
enabled: true
storageClassName: "default"
size: "1Gi"
-````
+```
@@ -150,17 +150,18 @@ To create the necessary database secrets, execute the following command:
Required Secrets
-````yaml
+```yaml
kubectl create secret generic db-encryption \
--from-literal=db-encryption-key='' \
--namespace homarr
-````
+```
-````yaml
+```yaml
kubectl create secret generic db-secret \
--from-literal=db-url='mysql://user:password@host:port/homarrdb' \
--namespace homarr
-````
+```
+
Below is an example of the override values file:
@@ -168,10 +169,11 @@ Below is an example of the override values file:
values.yaml
-````yaml
+```yaml
database:
type: mysql
-````
+```
+
#### External Postgresql database
@@ -181,17 +183,17 @@ To create the necessary database secrets, execute the following command:
Required Secrets
-````yaml
+```yaml
kubectl create secret generic db-encryption \
--from-literal=db-encryption-key='' \
--namespace homarr
-````
+```
-````yaml
+```yaml
kubectl create secret generic db-secret \
--from-literal=db-url='postgresql://user:password@host:port/homarrdb' \
--namespace homarr
-````
+```
@@ -200,10 +202,11 @@ Below is an example of the override values file:
values.yaml
-````yaml
+```yaml
database:
type: postgresql
-````
+```
+
### Ingress
@@ -213,7 +216,7 @@ The ingress section in the values.yaml file allows you to configure how external
values.yaml
-````yaml
+```yaml
service:
enabled: true # Ensure the service is enabled for Ingress to route traffic
ingress:
@@ -231,7 +234,8 @@ ingress:
- "homarr.homelab.dev"
- "www.homarr.homelab.dev"
secretName: homelab-tls
-````
+```
+
### HTTPRoute (Gateway API)
@@ -242,9 +246,9 @@ This provides a more expressive and future-proof alternative to Ingress, with su
values.yaml
-````yaml
+```yaml
service:
- enabled: true # must be enabled for HTTPRoute to forward traffic
+ enabled: true # must be enabled for HTTPRoute to forward traffic
httproute:
enabled: true
parentRefs:
@@ -260,14 +264,15 @@ httproute:
backendRefs:
- name: homarr
port: 7575
-````
+```
+
#### Notes:
- parentRefs: the Gateway(s) this route attaches to. Gateways must already exist.
- hostnames: domain names this route applies to.
-- rules: each rule can define matches (paths, headers, queries), optional filters, and backendRefs to services
+- rules: each rule can define matches (paths, headers, queries), optional filters, and backendRefs to services
- TLS must be configured on the Gateway (not on HTTPRoute). For example, the Gateway can have a TLS listener for homarr.homelab.dev, and this route will automatically apply once matched.
### Certificates
@@ -284,7 +289,7 @@ Use inline certificates to generate a ConfigMap, mounted as individual files.
values.yaml
-````yaml
+```yaml
persistence:
homarrTrustedCerts:
enabled: true
@@ -298,7 +303,8 @@ persistence:
-----BEGIN CERTIFICATE-----
MIID...EFGH==
-----END CERTIFICATE-----
-````
+```
+
Behavior:
@@ -313,7 +319,7 @@ Use inline certificates to generate a Kubernetes Secret, mounted as files.
values.yaml
-````yaml
+```yaml
persistence:
homarrTrustedCerts:
enabled: true
@@ -327,7 +333,8 @@ persistence:
-----BEGIN CERTIFICATE-----
MIIC...XYZ==
-----END CERTIFICATE-----
-````
+```
+
Behavior:
@@ -342,16 +349,17 @@ Use an existing Kubernetes Secret, assuming its keys are filenames.
values.yaml
-````yaml
+```yaml
persistence:
homarrTrustedCerts:
enabled: true
type: existingSecret
existingSecretName: "existingSecretName"
existingSecretKeys:
- - cert3.crt
- - cert4.crt
-````
+ - cert3.crt
+ - cert4.crt
+```
+
Behavior:
@@ -372,114 +380,115 @@ All available values are listed on the [artifacthub](https://artifacthub.io/pack
## Values
-| Key | Type | Default | Description |
-|-----|------|---------|-------------|
-| additionalObjects | list | `[]` | Additional resources to deploy. These objects are templated. |
-| affinity | object | `{}` | Node affinity for pod scheduling |
-| autoscaling.enabled | bool | `false` | Enable autoscaling |
-| autoscaling.maxReplicas | int | `100` | Maximum replicas |
-| autoscaling.minReplicas | int | `1` | Minimum replicas |
-| autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization for autoscaling |
-| containerPorts | object | `{"http":{"port":7575,"protocol":"TCP"}}` | containerPorts defines the ports to open on the container. It is a map where each entry specifies: - `port` (int) (required): The port number to expose inside the container. - `protocol` (string) (required): The network protocol (TCP or UDP) used for the port. - `disabled` (bool) : Optional flag to disable this port (defaults to false). Can be overridden via Helm values. By default, this configuration exposes TCP port 7575 with the name `http`. |
-| database.migrationEnabled | bool | `true` | Database migration configuration. DB_MIGRATIONS_DISABLED Set to `true` to disable database migrations. Migrations are enabled by default (`false`). |
-| database.type | string | `"sqlite"` | Database type: sqlite, mysql or postgresql |
-| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. |
-| env.AUTH_LDAP_BASE | string | `nil` | Base dn of your LDAP server |
-| env.AUTH_LDAP_BIND_DN | string | `nil` | User used for finding users and groups |
-| env.AUTH_LDAP_GROUP_CLASS | string | `"groupOfUniqueNames"` | Class used for querying groups |
-| env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user's groups search filter (& based) |
-| env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE | string | `"member"` | Attribute used for querying group member |
-| env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE | string | `"dn"` | User attribute used for comparing with group member |
-| env.AUTH_LDAP_SEARCH_SCOPE | string | `"base"` | LDAP search scope between base, one or sub |
-| env.AUTH_LDAP_URI | string | `nil` | URI of your LDAP server |
-| env.AUTH_LDAP_USERNAME_ATTRIBUTE | string | `"uid"` | Attribute used for username |
-| env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) |
-| env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field |
-| env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. |
-| env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login |
-| env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) |
-| env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim |
-| env.AUTH_OIDC_ISSUER | string | `nil` | Issuer URI of OIDC provider without trailing slash (/) |
-| env.AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE | string | `nil` | Overwrite name attribute. By default, it will use preferred_username if it does not contain a @ and otherwise name. |
-| env.AUTH_OIDC_SCOPE_OVERWRITE | string | `"openid email profile groups"` | Override the OIDC scopes |
-| env.AUTH_PROVIDERS | string | `"credentials"` | Enabled authentication methods. Multiple providers can be enabled with by separating them with , (ex. AUTH_PROVIDERS=credentials,oidc, it is highly recommended to just enable one provider). |
-| env.AUTH_SESSION_EXPIRY_TIME | string | `"30d"` | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") |
-| env.ENABLE_DNS_CACHING | string | `"false"` | Enables dns caching. This is not yet working for all users. See #4006 |
-| env.LOG_LEVEL | string | `"info"` | Log level to use. Possible values: debug/info/warn/error |
-| env.NO_EXTERNAL_CONNECTION | string | `"false"` | Disables some requests that need internet connection |
-| env.TZ | string | `"Europe/Paris"` | Your local time zone |
-| envSecrets.authLdapCredentials.existingSecret | string | `"auth-ldap-secret"` | Name of existing secret containing LDAP credentials |
-| envSecrets.authLdapCredentials.ldapBindingPassword | string | `"bind-password"` | Password for bind user secret key |
-| envSecrets.authOidcCredentials.existingSecret | string | `"auth-oidc-secret"` | Name of existing secret containing OIDC credentials |
-| envSecrets.authOidcCredentials.oidcClientId | string | `"oidc-client-id"` | ID of OIDC client (application) secret key |
-| envSecrets.authOidcCredentials.oidcClientSecret | string | `"oidc-client-secret"` | Secret of OIDC client (application) secret key |
-| envSecrets.dbCredentials.dbUrlKey | string | `"db-url"` | Secret key for DB_URL Example for external database: `mysql://username:password@host:port/homarrdb` or `postgresql://username:password@host:port/homarrdb` |
-| envSecrets.dbCredentials.existingSecret | string | `"db-secret"` | Name of existing secret containing DB credentials |
-| envSecrets.dbEncryption.existingSecret | string | `"db-encryption"` | Name of existing secret containing DB encryption |
-| envSecrets.dbEncryption.key | string | `"db-encryption-key"` | Secret key for SECRET_ENCRYPTION_KEY can be generated with `openssl rand -hex 32` |
-| fullnameOverride | string | `""` | Overrides chart's fullname |
-| hostAliases | list | `[]` | Add static entries to /etc/hosts in the Pod. This is useful in the following cases: - You are running in a dual-stack cluster (IPv4 + IPv6) and want to force usage of IPv4 for specific hostnames - Your application is having DNS resolution issues or IPv6 preference issues - You need to override or simulate DNS entries without changing global DNS - You are running in an air-gapped or isolated environment without external DNS Example: hostAliases: - ip: "192.168.1.10" hostnames: - "example.com" - "example.internal" |
-| httproute | object | `{"enabled":false,"hostnames":["chart-example.local"],"parentRefs":[{"name":"my-gateway","namespace":"default"}],"rules":[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}` | Gateway API HTTPRoute configuration |
-| httproute.enabled | bool | `false` | Enable HTTPRoute |
-| httproute.hostnames | list | `["chart-example.local"]` | Hostnames this route matches (similar to ingress.hosts.host) |
-| httproute.parentRefs | list | `[{"name":"my-gateway","namespace":"default"}]` | References to the parent Gateway(s) this route attaches to. Each item must include at least a `name`, and optionally a `namespace`. |
-| httproute.rules | list | `[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | List of routing rules. Each rule can include: - matches: path/header/query matching - filters: optional transformations (redirects, header modifications, etc.) - backendRefs: one or more Kubernetes Services to forward traffic to |
-| httproute.rules[0].filters | list | `[]` | Optional filters for this rule (default: empty) |
-| httproute.rules[0].matches[0].path.type | string | `"PathPrefix"` | Path match type. One of: Exact, PathPrefix, RegularExpression |
-| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
-| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
-| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
-| image.tag | string | `"v1.73.0"` | Overrides the image tag whose default is the chart appVersion |
-| imagePullSecrets | list | `[]` | Secrets for Docker registry |
-| ingress.annotations | object | `{}` | Ingress annotations |
-| ingress.enabled | bool | `false` | Enable ingress |
-| ingress.hosts | list | `[{"host":"chart-example.local","paths":[{"path":"/"}]}]` | Ingress hosts configuration |
-| ingress.ingressClassName | string | `""` | Ingress class name |
-| ingress.tls | list | `[]` | Ingress TLS configuration |
-| livenessProbe.failureThreshold | int | `3` | Failure threshold for liveness probe - number of consecutive failures before pod is restarted |
-| livenessProbe.httpGet.path | string | `"/api/health/live"` | This is the liveness check endpoint used by Kubernetes to determine if the application is still running. |
-| livenessProbe.httpGet.port | int | `7575` | The port on which the liveness check will be performed. This must be the same as the container port exposed by the application. |
-| livenessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the liveness probe starts |
-| livenessProbe.periodSeconds | int | `10` | Period in seconds between liveness probe checks |
-| livenessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each liveness probe check |
-| nameOverride | string | `""` | Overrides chart's name |
-| nodeSelector | object | `{}` | Node selectors for pod scheduling |
-| persistence.homarrDatabase.accessMode | string | `"ReadWriteOnce"` | homarr-database access mode |
-| persistence.homarrDatabase.enabled | bool | `false` | Enable homarr-database persistent storage |
-| persistence.homarrDatabase.mountPath | string | `"/appdata"` | homarr-database mount path inside the pod |
-| persistence.homarrDatabase.name | string | `"homarr-database"` | homarr-database persistent storage name |
-| persistence.homarrDatabase.size | string | `"50Mi"` | homarr-database storage size |
-| persistence.homarrDatabase.storageClassName | string | `"local-path"` | homarr-database storage class name |
-| persistence.homarrDatabase.volumeClaimName | string | `""` | homarr-database optional volumeClaimName to target specific PV |
-| persistence.homarrTrustedCerts.certificates | string | `nil` | homarr-trusted-certificates certificates, each entry will become a new trusted certificate as a dedicated file (works only for "configmap" and "secret" mode) |
-| persistence.homarrTrustedCerts.enabled | bool | `false` | Enable trusted certificates persistence |
-| persistence.homarrTrustedCerts.existingSecretKeys | string | `nil` | List of keys (filenames) to mount from the existing secret (used only when type is "existingSecret") |
-| persistence.homarrTrustedCerts.existingSecretName | string | `""` | Name of the existing Kubernetes Secret to mount (required if type is "existingSecret") |
-| persistence.homarrTrustedCerts.mountPath | string | `"/appdata/trusted-certificates"` | homarr-trusted-certificates mount path inside the pod |
-| persistence.homarrTrustedCerts.type | string | `"configmap"` | Persistence mode can be : configmap (declarative), secret (declarative) or existingSecret (mount an existing Kubernetes Secret by name and specify which keys to mount as files) |
-| podAnnotations | object | `{}` | Pod annotations |
-| podLabels | object | `{}` | Pod labels |
-| podSecurityContext | object | `{}` | Pod security context |
-| rbac | object | `{"enabled":false}` | Enable RBAC resources for Kubernetes integration Creates Role, ClusterRole, and associated bindings for Homarr's Kubernetes features |
-| rbac.enabled | bool | `false` | Enable to create RBAC resources and activate Kubernetes integration |
-| readinessProbe.failureThreshold | int | `3` | Failure threshold for readiness probe - number of consecutive failures before pod is considered unready |
-| readinessProbe.httpGet.path | string | `"/api/health/ready"` | This is the readiness check endpoint used by Kubernetes to determine if the application is ready to handle traffic. |
-| readinessProbe.httpGet.port | int | `7575` | The port on which the readiness check will be performed. This must match the container's exposed port. |
-| readinessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the readiness probe starts. increase this value if the pod is slow to fully start. |
-| readinessProbe.periodSeconds | int | `10` | Period in seconds between readiness probe checks |
-| readinessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each readiness probe check |
-| replicaCount | int | `1` | Number of replicas |
-| resources | object | `{}` | Resource configuration |
-| securityContext | object | `{}` | Security context |
-| service.enabled | bool | `true` | Enable service |
-| service.ipFamilies | list | `[]` | List of IP families to use for the service. Examples: - ["IPv4"] - ["IPv6"] - ["IPv4", "IPv6"] for dual-stack Leave empty to use cluster default behavior |
-| service.ipFamilyPolicy | string | `"SingleStack"` | Defines how the service assigns IP families (IPv4/IPv6) Possible values: - SingleStack (default): Only one IP family, usually IPv4 - PreferDualStack: Use dual-stack if the cluster supports it, fallback to single - RequireDualStack: Fail if dual-stack cannot be assigned |
-| service.ports.app.port | int | `7575` | Service port |
-| service.ports.app.protocol | string | `"TCP"` | Service protocol |
-| service.ports.app.targetPort | string | `"http"` | Service target port |
-| service.type | string | `"ClusterIP"` | Service type |
-| strategyType | string | `"RollingUpdate"` | `strategyType` specifies the strategy used to replace old Pods by new ones. `strategyType` can be `"Recreate"` or `"RollingUpdate"`. `"RollingUpdate"` is the default value and updates Pods in a rolling update fashion. `"Recreate"` will kill all existing Pods before new ones are created. The `"Recreate"` strategy is necessary when persistent volume's `accessMode` is set to `"ReadWriteOnce"` when using `helm upgrade`, as pod volume attachments to an existing PersistentVolumeClaim need to be cleared before a new pod can attach to it. |
-| tolerations | list | `[]` | Node tolerations for pod scheduling |
+| Key | Type | Default | Description |
+| -------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| additionalObjects | list | `[]` | Additional resources to deploy. These objects are templated. |
+| affinity | object | `{}` | Node affinity for pod scheduling |
+| autoscaling.enabled | bool | `false` | Enable autoscaling |
+| autoscaling.maxReplicas | int | `100` | Maximum replicas |
+| autoscaling.minReplicas | int | `1` | Minimum replicas |
+| autoscaling.targetCPUUtilizationPercentage | int | `80` | Target CPU utilization for autoscaling |
+| containerPorts | object | `{"http":{"port":7575,"protocol":"TCP"}}` | containerPorts defines the ports to open on the container. It is a map where each entry specifies: - `port` (int) (required): The port number to expose inside the container. - `protocol` (string) (required): The network protocol (TCP or UDP) used for the port. - `disabled` (bool) : Optional flag to disable this port (defaults to false). Can be overridden via Helm values. By default, this configuration exposes TCP port 7575 with the name `http`. |
+| database.migrationEnabled | bool | `true` | Database migration configuration. DB_MIGRATIONS_DISABLED Set to `true` to disable database migrations. Migrations are enabled by default (`false`). |
+| database.type | string | `"sqlite"` | Database type: sqlite, mysql or postgresql |
+| env.AUTH_COOKIE_PREFIX | string | `"homarr"` | Prefix used for all authentication cookies. Change if you run another Auth.js/NextAuth app on the same hostname to avoid cookie name collisions. Only letters, numbers, hyphens and underscores. |
+| env.AUTH_LDAP_BASE | string | `nil` | Base dn of your LDAP server |
+| env.AUTH_LDAP_BIND_DN | string | `nil` | User used for finding users and groups |
+| env.AUTH_LDAP_GROUP_CLASS | string | `"groupOfUniqueNames"` | Class used for querying groups |
+| env.AUTH_LDAP_GROUP_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user's groups search filter (& based) |
+| env.AUTH_LDAP_GROUP_MEMBER_ATTRIBUTE | string | `"member"` | Attribute used for querying group member |
+| env.AUTH_LDAP_GROUP_MEMBER_USER_ATTRIBUTE | string | `"dn"` | User attribute used for comparing with group member |
+| env.AUTH_LDAP_SEARCH_SCOPE | string | `"base"` | LDAP search scope between base, one or sub |
+| env.AUTH_LDAP_URI | string | `nil` | URI of your LDAP server |
+| env.AUTH_LDAP_USERNAME_ATTRIBUTE | string | `"uid"` | Attribute used for username |
+| env.AUTH_LDAP_USERNAME_FILTER_EXTRA_ARG | string | `nil` | Extra arguments for user search filter (& based) |
+| env.AUTH_LDAP_USER_MAIL_ATTRIBUTE | string | `"mail"` | Attribute used for mail field |
+| env.AUTH_LOGOUT_REDIRECT_URL | string | `nil` | URL to redirect to after clicking logging out. |
+| env.AUTH_OIDC_AUTO_LOGIN | string | `"false"` | Automatically redirect to OIDC login |
+| env.AUTH_OIDC_CLIENT_NAME | string | `"OIDC"` | Display name of provider (in login screen) |
+| env.AUTH_OIDC_GROUPS_ATTRIBUTE | string | `"groups"` | Attribute used for groups (roles) claim |
+| env.AUTH_OIDC_ISSUER | string | `nil` | Issuer URI of OIDC provider without trailing slash (/) |
+| env.AUTH_OIDC_NAME_ATTRIBUTE_OVERWRITE | string | `nil` | Overwrite name attribute. By default, it will use preferred_username if it does not contain a @ and otherwise name. |
+| env.AUTH_OIDC_SCOPE_OVERWRITE | string | `"openid email profile groups"` | Override the OIDC scopes |
+| env.AUTH_PROVIDERS | string | `"credentials"` | Enabled authentication methods. Multiple providers can be enabled with by separating them with , (ex. AUTH_PROVIDERS=credentials,oidc, it is highly recommended to just enable one provider). |
+| env.AUTH_SESSION_EXPIRY_TIME | string | `"30d"` | Time for the session to time out. Can be set as pure number, which will automatically be used in seconds, or followed by s, m, h or d for seconds, minutes, hours or days. (ex: "30m") |
+| env.ENABLE_DNS_CACHING | string | `"false"` | Enables dns caching. This is not yet working for all users. See #4006 |
+| env.LOG_LEVEL | string | `"info"` | Log level to use. Possible values: debug/info/warn/error |
+| env.NO_EXTERNAL_CONNECTION | string | `"false"` | Disables some requests that need internet connection |
+| env.TZ | string | `"Europe/Paris"` | Your local time zone |
+| envSecrets.authLdapCredentials.existingSecret | string | `"auth-ldap-secret"` | Name of existing secret containing LDAP credentials |
+| envSecrets.authLdapCredentials.ldapBindingPassword | string | `"bind-password"` | Password for bind user secret key |
+| envSecrets.authOidcCredentials.existingSecret | string | `"auth-oidc-secret"` | Name of existing secret containing OIDC credentials |
+| envSecrets.authOidcCredentials.oidcClientId | string | `"oidc-client-id"` | ID of OIDC client (application) secret key |
+| envSecrets.authOidcCredentials.oidcClientSecret | string | `"oidc-client-secret"` | Secret of OIDC client (application) secret key |
+| envSecrets.dbCredentials.dbUrlKey | string | `"db-url"` | Secret key for DB_URL Example for external database: `mysql://username:password@host:port/homarrdb` or `postgresql://username:password@host:port/homarrdb` |
+| envSecrets.dbCredentials.existingSecret | string | `"db-secret"` | Name of existing secret containing DB credentials |
+| envSecrets.dbEncryption.existingSecret | string | `"db-encryption"` | Name of existing secret containing DB encryption |
+| envSecrets.dbEncryption.key | string | `"db-encryption-key"` | Secret key for SECRET_ENCRYPTION_KEY can be generated with `openssl rand -hex 32` |
+| fullnameOverride | string | `""` | Overrides chart's fullname |
+| hostAliases | list | `[]` | Add static entries to /etc/hosts in the Pod. This is useful in the following cases: - You are running in a dual-stack cluster (IPv4 + IPv6) and want to force usage of IPv4 for specific hostnames - Your application is having DNS resolution issues or IPv6 preference issues - You need to override or simulate DNS entries without changing global DNS - You are running in an air-gapped or isolated environment without external DNS Example: hostAliases: - ip: "192.168.1.10" hostnames: - "example.com" - "example.internal" |
+| httproute | object | `{"enabled":false,"hostnames":["chart-example.local"],"parentRefs":[{"name":"my-gateway","namespace":"default"}],"rules":[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]}` | Gateway API HTTPRoute configuration |
+| httproute.enabled | bool | `false` | Enable HTTPRoute |
+| httproute.hostnames | list | `["chart-example.local"]` | Hostnames this route matches (similar to ingress.hosts.host) |
+| httproute.parentRefs | list | `[{"name":"my-gateway","namespace":"default"}]` | References to the parent Gateway(s) this route attaches to. Each item must include at least a `name`, and optionally a `namespace`. |
+| httproute.rules | list | `[{"backendRefs":[{"name":"homarr","port":7575}],"filters":[],"matches":[{"path":{"type":"PathPrefix","value":"/"}}]}]` | List of routing rules. Each rule can include: - matches: path/header/query matching - filters: optional transformations (redirects, header modifications, etc.) - backendRefs: one or more Kubernetes Services to forward traffic to |
+| httproute.rules[0].filters | list | `[]` | Optional filters for this rule (default: empty) |
+| httproute.rules[0].matches[0].path.type | string | `"PathPrefix"` | Path match type. One of: Exact, PathPrefix, RegularExpression |
+| httproute.rules[0].matches[0].path.value | string | `"/"` | Path value to match |
+| image.pullPolicy | string | `"IfNotPresent"` | Image pull policy |
+| image.repository | string | `"ghcr.io/homarr-labs/homarr"` | Image repository |
+| image.tag | string | `"v1.74.0"` | Overrides the image tag whose default is the chart appVersion |
+| imagePullSecrets | list | `[]` | Secrets for Docker registry |
+| ingress.annotations | object | `{}` | Ingress annotations |
+| ingress.enabled | bool | `false` | Enable ingress |
+| ingress.hosts | list | `[{"host":"chart-example.local","paths":[{"path":"/"}]}]` | Ingress hosts configuration |
+| ingress.ingressClassName | string | `""` | Ingress class name |
+| ingress.tls | list | `[]` | Ingress TLS configuration |
+| livenessProbe.failureThreshold | int | `3` | Failure threshold for liveness probe - number of consecutive failures before pod is restarted |
+| livenessProbe.httpGet.path | string | `"/api/health/live"` | This is the liveness check endpoint used by Kubernetes to determine if the application is still running. |
+| livenessProbe.httpGet.port | int | `7575` | The port on which the liveness check will be performed. This must be the same as the container port exposed by the application. |
+| livenessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the liveness probe starts |
+| livenessProbe.periodSeconds | int | `10` | Period in seconds between liveness probe checks |
+| livenessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each liveness probe check |
+| nameOverride | string | `""` | Overrides chart's name |
+| nodeSelector | object | `{}` | Node selectors for pod scheduling |
+| persistence.homarrDatabase.accessMode | string | `"ReadWriteOnce"` | homarr-database access mode |
+| persistence.homarrDatabase.enabled | bool | `false` | Enable homarr-database persistent storage |
+| persistence.homarrDatabase.mountPath | string | `"/appdata"` | homarr-database mount path inside the pod |
+| persistence.homarrDatabase.name | string | `"homarr-database"` | homarr-database persistent storage name |
+| persistence.homarrDatabase.size | string | `"50Mi"` | homarr-database storage size |
+| persistence.homarrDatabase.storageClassName | string | `"local-path"` | homarr-database storage class name |
+| persistence.homarrDatabase.volumeClaimName | string | `""` | homarr-database optional volumeClaimName to target specific PV |
+| persistence.homarrTrustedCerts.certificates | string | `nil` | homarr-trusted-certificates certificates, each entry will become a new trusted certificate as a dedicated file (works only for "configmap" and "secret" mode) |
+| persistence.homarrTrustedCerts.enabled | bool | `false` | Enable trusted certificates persistence |
+| persistence.homarrTrustedCerts.existingSecretKeys | string | `nil` | List of keys (filenames) to mount from the existing secret (used only when type is "existingSecret") |
+| persistence.homarrTrustedCerts.existingSecretName | string | `""` | Name of the existing Kubernetes Secret to mount (required if type is "existingSecret") |
+| persistence.homarrTrustedCerts.mountPath | string | `"/appdata/trusted-certificates"` | homarr-trusted-certificates mount path inside the pod |
+| persistence.homarrTrustedCerts.type | string | `"configmap"` | Persistence mode can be : configmap (declarative), secret (declarative) or existingSecret (mount an existing Kubernetes Secret by name and specify which keys to mount as files) |
+| podAnnotations | object | `{}` | Pod annotations |
+| podLabels | object | `{}` | Pod labels |
+| podSecurityContext | object | `{}` | Pod security context |
+| rbac | object | `{"enabled":false}` | Enable RBAC resources for Kubernetes integration Creates Role, ClusterRole, and associated bindings for Homarr's Kubernetes features |
+| rbac.enabled | bool | `false` | Enable to create RBAC resources and activate Kubernetes integration |
+| readinessProbe.failureThreshold | int | `3` | Failure threshold for readiness probe - number of consecutive failures before pod is considered unready |
+| readinessProbe.httpGet.path | string | `"/api/health/ready"` | This is the readiness check endpoint used by Kubernetes to determine if the application is ready to handle traffic. |
+| readinessProbe.httpGet.port | int | `7575` | The port on which the readiness check will be performed. This must match the container's exposed port. |
+| readinessProbe.initialDelaySeconds | int | `10` | Initial delay in seconds before the readiness probe starts. increase this value if the pod is slow to fully start. |
+| readinessProbe.periodSeconds | int | `10` | Period in seconds between readiness probe checks |
+| readinessProbe.timeoutSeconds | int | `1` | Timeout in seconds for each readiness probe check |
+| replicaCount | int | `1` | Number of replicas |
+| resources | object | `{}` | Resource configuration |
+| securityContext | object | `{}` | Security context |
+| service.enabled | bool | `true` | Enable service |
+| service.ipFamilies | list | `[]` | List of IP families to use for the service. Examples: - ["IPv4"] - ["IPv6"] - ["IPv4", "IPv6"] for dual-stack Leave empty to use cluster default behavior |
+| service.ipFamilyPolicy | string | `"SingleStack"` | Defines how the service assigns IP families (IPv4/IPv6) Possible values: - SingleStack (default): Only one IP family, usually IPv4 - PreferDualStack: Use dual-stack if the cluster supports it, fallback to single - RequireDualStack: Fail if dual-stack cannot be assigned |
+| service.ports.app.port | int | `7575` | Service port |
+| service.ports.app.protocol | string | `"TCP"` | Service protocol |
+| service.ports.app.targetPort | string | `"http"` | Service target port |
+| service.type | string | `"ClusterIP"` | Service type |
+| strategyType | string | `"RollingUpdate"` | `strategyType` specifies the strategy used to replace old Pods by new ones. `strategyType` can be `"Recreate"` or `"RollingUpdate"`. `"RollingUpdate"` is the default value and updates Pods in a rolling update fashion. `"Recreate"` will kill all existing Pods before new ones are created. The `"Recreate"` strategy is necessary when persistent volume's `accessMode` is set to `"ReadWriteOnce"` when using `helm upgrade`, as pod volume attachments to an existing PersistentVolumeClaim need to be cleared before a new pod can attach to it. |
+| tolerations | list | `[]` | Node tolerations for pod scheduling |
---
+
Autogenerated from chart metadata using [helm-docs](https://github.com/norwoodj/helm-docs)
diff --git a/apps/docs/docs/getting-started/installation/home-assistant.mdx b/apps/docs/docs/getting-started/installation/home-assistant.mdx
index 9f5a50f52..9ba9c5d01 100644
--- a/apps/docs/docs/getting-started/installation/home-assistant.mdx
+++ b/apps/docs/docs/getting-started/installation/home-assistant.mdx
@@ -14,7 +14,7 @@ Homarr can be seamlessly integrated into your Home Assistant setup with this add
## Installation
1. **Add Repository**: Navigate to your Home Assistant Supervisor panel, and go to the Add-on Store. Add the following repository URL: `https://github.com/Wiggen94/ha-homarr-v1-server` or click this button
-[](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2FWiggen94%2Fha-homarr-v1-server)
+ [](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2FWiggen94%2Fha-homarr-v1-server)
2. **Install the Add-on**: Search for "Homarr Server" in the Add-on Store and select it. Click on "Install" to start the installation process.
diff --git a/apps/docs/docs/getting-started/installation/portainer.mdx b/apps/docs/docs/getting-started/installation/portainer.mdx
index f816d77fe..0442b215e 100644
--- a/apps/docs/docs/getting-started/installation/portainer.mdx
+++ b/apps/docs/docs/getting-started/installation/portainer.mdx
@@ -1,5 +1,7 @@
# Portainer
+
## Portainer Stacks
+
### Prerequisites
- [Portainer Stacks](https://www.portainer.io/blog/stacks-docker-compose-the-portainer-way)
@@ -23,15 +25,14 @@ services:
environment:
- SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
ports:
- - '7575:7575'
+ - "7575:7575"
```
-
:::note
-Portainer will mount the files to your host machine. Please make sure to replace ```` from the docker run command with your desired storage location. The path must be absolute.
+Portainer will mount the files to your host machine. Please make sure to replace `` from the docker run command with your desired storage location. The path must be absolute.
:::
-Then, click on ``Deploy the stack`` and wait until the container has started.
+Then, click on `Deploy the stack` and wait until the container has started.
:::tip
@@ -43,5 +44,5 @@ If you're unsure how to create a stack, checkout the [Portainer Documentation](h
To update Homarr using Docker Compose or Portainer Stacks, simply pull the latest image and restart the container.
-1. Run ``docker pull ghcr.io/homarr-labs/homarr:latest`` to pull the latest image.
-2. Re-run ``docker-compose up -d`` or re-deploy the stack in Portainer.
+1. Run `docker pull ghcr.io/homarr-labs/homarr:latest` to pull the latest image.
+2. Re-run `docker-compose up -d` or re-deploy the stack in Portainer.
diff --git a/apps/docs/docs/getting-started/installation/proxmox.mdx b/apps/docs/docs/getting-started/installation/proxmox.mdx
index f6f5fb120..bd33b4040 100644
--- a/apps/docs/docs/getting-started/installation/proxmox.mdx
+++ b/apps/docs/docs/getting-started/installation/proxmox.mdx
@@ -31,7 +31,7 @@ After you've installed Homarr, you can find a new LXC "homarr".
# Updating
-Updating is as simple as executing
+Updating is as simple as executing
```bash
update
@@ -49,17 +49,19 @@ Some Homarr features require setting environment variables. When installing Homa
# Troubleshooting
-If something does not work after an update, please first try rebooting the LXC or the homarr Service with
+If something does not work after an update, please first try rebooting the LXC or the homarr Service with
```bash
systemctl restart homarr
```
# Getting help
+
- If you are looking for help with the script, updating the LXC or troubleshooting the installer, please get in contact with the support of the community scripts. They provide support on their [Discord](https://discord.gg/2wvnMDgdnU). If you found vulnerabilities, bugs or have a request for the script, [create a new issue here](https://github.com/community-scripts/ProxmoxVE/issues/new/choose).
- If you are looking to report a bug, vulnerability or request a new feature, use https://github.com/homarr-labs/homarr . You can ask for help on our Discord.
# How to find logs
+
Homarr runs under a system service called `homarr`, so getting the logs is a simple as using the following command inside the LXC (Container) Shell
```bash
diff --git a/apps/docs/docs/getting-started/installation/qnap.mdx b/apps/docs/docs/getting-started/installation/qnap.mdx
index a655db5b3..b8c1f977b 100644
--- a/apps/docs/docs/getting-started/installation/qnap.mdx
+++ b/apps/docs/docs/getting-started/installation/qnap.mdx
@@ -4,6 +4,6 @@
QNAP installation guide has not updated to 1.0 yet. There guide is still for 0.15.10.
:::
-When installing on QNAP, we recommend you to install Homarr using [Container Station]( https://www.qnap.com/en/software/container-station) or [this third-party image](https://www.myqnap.org/product/homarr/).
+When installing on QNAP, we recommend you to install Homarr using [Container Station](https://www.qnap.com/en/software/container-station) or [this third-party image](https://www.myqnap.org/product/homarr/).
There is also a guide available, but note that it is written in Chinese: https://post.smzdm.com/p/awzm7op2/
diff --git a/apps/docs/docs/getting-started/installation/runtipi.mdx b/apps/docs/docs/getting-started/installation/runtipi.mdx
index 53a669c90..c2b2823b7 100644
--- a/apps/docs/docs/getting-started/installation/runtipi.mdx
+++ b/apps/docs/docs/getting-started/installation/runtipi.mdx
@@ -1,4 +1,4 @@
-# Runtipi
+# Runtipi
Runtipi is a self-hosted orchestrator that makes it easy to manage and run multiple services on a single server. It is based on Docker and comes with a simple web interface to manage your services. Runtipi is designed to be easy to use, so you don't have to worry about manual configuration or networking.
@@ -11,6 +11,7 @@ https://runtipi.io
# Installation
Installing Homarr is pretty untuitive, the app is available in the default appstore :
+
1. Search for **"Homarr"** in the App-Store page and select it
2. Click on **"Install"**.
3. Choose the method you want to access Homarr, through any port or domain name then select **"Install"**
@@ -18,13 +19,15 @@ Installing Homarr is pretty untuitive, the app is available in the default appst
# Maintenance
After installation, all you need is available from the WebUI in **"My apps"** > **"Homarr"** :
- - Start/Stop
- - Update
- - Backup
- - Logs
-
+
+- Start/Stop
+- Update
+- Backup
+- Logs
+
If you like to tinker with your apps, you can also edit your app's docker-compose with a [user-config](https://runtipi.io/docs/guides/customize-app-config).
# Getting help
+
- If you are looking for help with anything in Runtipi. They provide support on their [Discord](https://discord.gg/Bu9qEPnHsc) and [forums](https://forums.runtipi.io/). If you found vulnerabilities or bugs, [create a new issue here](https://github.com/runtipi/runtipi-appstore/issues).
-- If you are looking to report a bug, vulnerability or request a new feature for Homarr, use https://github.com/homarr-labs/homarr . You can ask for help on our Discord.
\ No newline at end of file
+- If you are looking to report a bug, vulnerability or request a new feature for Homarr, use https://github.com/homarr-labs/homarr . You can ask for help on our Discord.
diff --git a/apps/docs/docs/getting-started/installation/saltbox.mdx b/apps/docs/docs/getting-started/installation/saltbox.mdx
index 2f284d72b..88805eafb 100644
--- a/apps/docs/docs/getting-started/installation/saltbox.mdx
+++ b/apps/docs/docs/getting-started/installation/saltbox.mdx
@@ -5,6 +5,7 @@ Saltbox installation command has not updated to 1.0 yet. There command still run
:::
You can also use this to install on Saltbox:
+
```bash
sb install sandbox-homarr
-```
\ No newline at end of file
+```
diff --git a/apps/docs/docs/getting-started/installation/source.mdx b/apps/docs/docs/getting-started/installation/source.mdx
index 933b264dd..838f0d0a2 100644
--- a/apps/docs/docs/getting-started/installation/source.mdx
+++ b/apps/docs/docs/getting-started/installation/source.mdx
@@ -28,15 +28,29 @@ This method is only useful, when you want to develop on Homarr or extend it with
## Steps
-
Clone the Repository using git clone https://github.com/homarr-labs/homarr.git
-
Enter the created directory using cd homarr
-
Install all dependencies using pnpm install
-
Copy the `.env.example` file to `.env` and replace `DB_URL` with the full path you want to use for the sqlite database file. For example `DB_URL='/home/username/homarr/db.sqlite`
-
Replace `AUTH_SECRET` and `SECRET_ENCRYPTION_KEY` both with a secure random string by using `openssl rand -hex 32`
+
+ Clone the Repository using git clone https://github.com/homarr-labs/homarr.git
+
+
+ Enter the created directory using cd homarr
+
+
+ Install all dependencies using pnpm install
+
+
+ Copy the `.env.example` file to `.env` and replace `DB_URL` with the full path you want to use for the sqlite
+ database file. For example `DB_URL='/home/username/homarr/db.sqlite`
+
+
+ Replace `AUTH_SECRET` and `SECRET_ENCRYPTION_KEY` both with a secure random string by using `openssl rand -hex 32`
+
Replace `CRON_JOB_API_KEY` with a secure random string by using `openssl rand -base64 32`
Run `pnpm run db:migration:sqlite:run` and wait that it completes
Build the source using `pnpm build`
-
Copy better-sqlite3.node files with `mkdir build` and `cp ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node`
+
+ Copy better-sqlite3.node files with `mkdir build` and `cp
+ ./node_modules/better-sqlite3/build/Release/better_sqlite3.node ./build/better_sqlite3.node`
+
Start a redis server using `redis-server`
Run the server using `pnpm start`
Open `http://localhost:3000` in your browser
diff --git a/apps/docs/docs/getting-started/installation/unraid.mdx b/apps/docs/docs/getting-started/installation/unraid.mdx
index 5fa7cc9b0..18588c5d4 100644
--- a/apps/docs/docs/getting-started/installation/unraid.mdx
+++ b/apps/docs/docs/getting-started/installation/unraid.mdx
@@ -1,5 +1,5 @@
-import Link from '@docusaurus/Link';
-import { IconDownload } from '@tabler/icons-react';
+import Link from "@docusaurus/Link";
+import { IconDownload } from "@tabler/icons-react";
# Unraid
@@ -14,17 +14,17 @@ You can install Homarr directly from your Unraid Dashboard, no terminal required
-
+
Install community applications plugin
-
-
+
+
:::tip
@@ -46,4 +46,3 @@ After you've installed the Community Apps Plugin, you can install Homarr from th
After you've installed Homarr, you can find it under the tab "Docker".
The official support thread is located [here](https://forums.unraid.net/topic/123478-support-smartphonelover-homarr/), but we prefer to communicate over GitHub and Discord instead.
-
diff --git a/apps/docs/docs/integrations/adguard-home/index.mdx b/apps/docs/docs/integrations/adguard-home/index.mdx
index b4cb6cdb6..52b26f66f 100644
--- a/apps/docs/docs/integrations/adguard-home/index.mdx
+++ b/apps/docs/docs/integrations/adguard-home/index.mdx
@@ -1,37 +1,44 @@
---
-title: 'AdGuard Home'
+title: "AdGuard Home"
description: "AdGuard Home is a network-wide software for blocking ads and trackers, providing a safer and faster internet experience."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconAd, IconDeviceGamepad } from "@tabler/icons-react";
-import { adguardHomeIntegration } from '.';
-import { dnsHoleSummaryWidget } from '@site/docs/widgets/dns-hole-summary';
-import { dnsHoleControlsWidget } from '@site/docs/widgets/dns-hole-controls';
+import { adguardHomeIntegration } from ".";
+import { dnsHoleSummaryWidget } from "@site/docs/widgets/dns-hole-summary";
+import { dnsHoleControlsWidget } from "@site/docs/widgets/dns-hole-controls";
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/anchor/index.mdx b/apps/docs/docs/integrations/anchor/index.mdx
index e4d897767..2e43195cc 100644
--- a/apps/docs/docs/integrations/anchor/index.mdx
+++ b/apps/docs/docs/integrations/anchor/index.mdx
@@ -1,40 +1,45 @@
---
-title: 'Anchor'
+title: "Anchor"
description: "Anchor is a self-hosted note-taking application."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { anchorIntegration } from '.';
-import { anchorNoteWidget } from '@site/docs/widgets/anchor-note';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { anchorIntegration } from ".";
+import { anchorNoteWidget } from "@site/docs/widgets/anchor-note";
-
-
+
The Anchor integration lets Homarr connect to your Anchor instance so you can display and edit selected notes directly from your dashboard.
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/aria2/index.mdx b/apps/docs/docs/integrations/aria2/index.mdx
index 9950fec2f..8ec92a5d0 100644
--- a/apps/docs/docs/integrations/aria2/index.mdx
+++ b/apps/docs/docs/integrations/aria2/index.mdx
@@ -1,38 +1,43 @@
---
-title: 'Aria2'
+title: "Aria2"
description: "Lightweight multiprotocol & multi-source command-line download utility. It supports HTTP/HTTPS, FTP, SFTP, BitTorrent and Metalink"
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { aria2Integration } from '.';
-import { downloadsWidget } from '@site/docs/widgets/downloads';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { aria2Integration } from ".";
+import { downloadsWidget } from "@site/docs/widgets/downloads";
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/audiobookshelf/index.mdx b/apps/docs/docs/integrations/audiobookshelf/index.mdx
index 156525ba5..a052d2fff 100644
--- a/apps/docs/docs/integrations/audiobookshelf/index.mdx
+++ b/apps/docs/docs/integrations/audiobookshelf/index.mdx
@@ -1,41 +1,40 @@
---
-title: 'Audiobookshelf'
+title: "Audiobookshelf"
description: "Audiobookshelf is a self-hosted audiobook and podcast server that lets you manage and stream your audio library."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { audiobookshelfIntegration } from '.';
-import { audioStatsWidget } from '@site/docs/widgets/audio-stats';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { audiobookshelfIntegration } from ".";
+import { audioStatsWidget } from "@site/docs/widgets/audio-stats";
-
-
+
Audiobookshelf provides library statistics including audiobook and podcast counts, listening time, and active sessions.
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/bazarr/index.mdx b/apps/docs/docs/integrations/bazarr/index.mdx
index f9400a45e..7e1ad2d64 100644
--- a/apps/docs/docs/integrations/bazarr/index.mdx
+++ b/apps/docs/docs/integrations/bazarr/index.mdx
@@ -1,31 +1,30 @@
---
-title: 'Bazarr'
+title: "Bazarr"
description: "Bazarr is a companion application to Sonarr and Radarr that manages and downloads subtitles."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { bazarrIntegration } from '.';
-import { bazarrWidget } from '@site/docs/widgets/bazarr';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { bazarrIntegration } from ".";
+import { bazarrWidget } from "@site/docs/widgets/bazarr";
-
+
Bazarr complements Sonarr and Radarr by managing subtitle downloads. The Homarr integration uses your Bazarr API key to fetch missing subtitle counts and health badges.
### Screenshots
-import apiKeySettings from './img/api-key-settings.png';
-import widgetOverview from '@site/docs/widgets/bazarr/img/widget-overview.png';
+import apiKeySettings from "./img/api-key-settings.png";
+import widgetOverview from "@site/docs/widgets/bazarr/img/widget-overview.png";
-
API key location in Bazarr (Settings → General → Security):
+
+ API key location in Bazarr (Settings → General → Security):
+
@@ -35,20 +34,25 @@ import widgetOverview from '@site/docs/widgets/bazarr/img/widget-overview.png';
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
+
You can find your Bazarr API key under **Settings → General → Security**.
-
+
diff --git a/apps/docs/docs/integrations/beszel/index.mdx b/apps/docs/docs/integrations/beszel/index.mdx
index 0c93cdc9f..8ddda77c6 100644
--- a/apps/docs/docs/integrations/beszel/index.mdx
+++ b/apps/docs/docs/integrations/beszel/index.mdx
@@ -1,25 +1,22 @@
---
-title: 'Beszel'
+title: "Beszel"
description: "Beszel is a lightweight server monitoring platform with Docker stats, historical data, and alert functions."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { beszelIntegration } from '.';
-import { beszelSystemStatsWidget } from '@site/docs/widgets/beszel-system-stats';
-import { beszelSystemGridWidget } from '@site/docs/widgets/beszel-system-grid';
-import { beszelSystemTableWidget } from '@site/docs/widgets/beszel-system-table';
-import { beszelAlertsWidget } from '@site/docs/widgets/beszel-alerts';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { beszelIntegration } from ".";
+import { beszelSystemStatsWidget } from "@site/docs/widgets/beszel-system-stats";
+import { beszelSystemGridWidget } from "@site/docs/widgets/beszel-system-grid";
+import { beszelSystemTableWidget } from "@site/docs/widgets/beszel-system-table";
+import { beszelAlertsWidget } from "@site/docs/widgets/beszel-alerts";
-import beszelDashboard from './img/beszel-dashboard.png';
+import beszelDashboard from "./img/beszel-dashboard.png";
-
+
Beszel monitors your servers in real time via lightweight agents, collecting CPU, memory, disk, network, GPU, temperature, and Docker container metrics. It stores historical data and supports configurable alerts.
@@ -28,26 +25,33 @@ Beszel monitors your servers in real time via lightweight agents, collecting CPU
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
+
+
:::tip
Beszel uses PocketBase under the hood. The default admin credentials are set during the initial Beszel setup.
diff --git a/apps/docs/docs/integrations/coolify/index.mdx b/apps/docs/docs/integrations/coolify/index.mdx
index 56e5e6c29..7b828cc18 100644
--- a/apps/docs/docs/integrations/coolify/index.mdx
+++ b/apps/docs/docs/integrations/coolify/index.mdx
@@ -1,29 +1,24 @@
---
-title: 'Coolify'
-description: 'Self-hosted PaaS and deployment platform'
+title: "Coolify"
+description: "Self-hosted PaaS and deployment platform"
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { coolifyIntegration } from '.';
-import { coolifyWidget } from '../../widgets/coolify';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { coolifyIntegration } from ".";
+import { coolifyWidget } from "../../widgets/coolify";
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
:::info URL configuration
@@ -31,16 +26,28 @@ Use the root URL of your Coolify instance, for example `https://coolify.example.
:::
### Secrets
-Enable API access under Settings > Advanced if it is disabled.),
- (Go to Security > API Tokens.),
- 'Enter a name, e.g. "Homarr".',
- (Select the read permission. The Coolify widget only reads version, server, project, application, and service data.),
- 'Click "Create".',
- 'Copy the generated token immediately. Coolify only shows it once.',
- 'Paste the token into the API Key field in Homarr.'
- ]
-}]} />
+
+
+ Enable API access under Settings > Advanced if it is disabled.
+ ,
+
+ Go to Security > API Tokens.
+ ,
+ 'Enter a name, e.g. "Homarr".',
+
+ Select the read permission. The Coolify widget only reads version, server, project, application,
+ and service data.
+ ,
+ 'Click "Create".',
+ "Copy the generated token immediately. Coolify only shows it once.",
+ "Paste the token into the API Key field in Homarr.",
+ ],
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/dash-dot/index.mdx b/apps/docs/docs/integrations/dash-dot/index.mdx
index 512bc6c8d..9c400321c 100644
--- a/apps/docs/docs/integrations/dash-dot/index.mdx
+++ b/apps/docs/docs/integrations/dash-dot/index.mdx
@@ -1,32 +1,33 @@
---
-title: 'Dash.'
+title: "Dash."
description: "Dash. is a system performance and resource monitoring tool"
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconHeartRateMonitor } from "@tabler/icons-react";
-import { dashDotIntegration } from '.';
-import { healthMonitoringWidget } from '@site/docs/widgets/health-monitoring';
-import { systemResourcesWidget } from '@site/docs/widgets/system-resources';
+import { dashDotIntegration } from ".";
+import { healthMonitoringWidget } from "@site/docs/widgets/health-monitoring";
+import { systemResourcesWidget } from "@site/docs/widgets/system-resources";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/deluge/index.mdx b/apps/docs/docs/integrations/deluge/index.mdx
index 24aadb910..89f906c0c 100644
--- a/apps/docs/docs/integrations/deluge/index.mdx
+++ b/apps/docs/docs/integrations/deluge/index.mdx
@@ -1,36 +1,41 @@
---
-title: 'Deluge'
+title: "Deluge"
description: "Deluge is a lightweight, Free Software, cross-platform BitTorrent client."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconDownload } from "@tabler/icons-react";
-import Alert from '@theme/Admonition';
-import { delugeIntegration } from '.';
-import { downloadsWidget } from '@site/docs/widgets/downloads';
+import Alert from "@theme/Admonition";
+import { delugeIntegration } from ".";
+import { downloadsWidget } from "@site/docs/widgets/downloads";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/docker/index.mdx b/apps/docs/docs/integrations/docker/index.mdx
index 2af43c67e..40ade4912 100644
--- a/apps/docs/docs/integrations/docker/index.mdx
+++ b/apps/docs/docs/integrations/docker/index.mdx
@@ -1,32 +1,31 @@
---
-title: 'Docker'
+title: "Docker"
description: "Docker is a platform where you can run your applications in containers, allowing for easy deployment and management of applications."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconBrandDocker, IconTool } from "@tabler/icons-react";
-import { dockerIntegration } from '.';
-import { dockerContainersWidget } from '@site/docs/widgets/docker-containers';
+import { dockerIntegration } from ".";
+import { dockerContainersWidget } from "@site/docs/widgets/docker-containers";
-
-
+
### Widgets & Capabilities
+
-
## Enabling the Docker integration
+
This integration must first be enabled.
If you directly run on the host system and you have docker installed, it will be enabled by default.
Otherwise, you'll need to mount the [docker socket](https://docs.docker.com/engine/security/protect-access/) to the Homarr container,
@@ -49,7 +48,7 @@ services:
environment:
- SECRET_ENCRYPTION_KEY=your_64_character_hex_string # <--- can be generated with `openssl rand -hex 32`
ports:
- - '7575:7575'
+ - "7575:7575"
```
With docker run, add this parameter: `-v /var/run/docker.sock:/var/run/docker.sock`.
@@ -124,8 +123,10 @@ services:
```
### Managing your Docker containers within Homarr
+
Homarr allows you to interact with Docker containers running on your system.
Each container row has an action menu with the following options:
+
- **View logs** - opens a dedicated logs page with a live terminal
- **Start** - start a stopped container
- **Stop** - stop a running container
@@ -166,11 +167,13 @@ Currently Homarr supports one label for configuration of the containers:
- **homarr.hide:** If set to any value, the container will be hidden from the Docker Containers widget and tools page.
### Security
+
Mounting docker sockets can be risky, as they permit full control over your docker service.
As an example, a thread actor could abuse Homarr use the socket to start, stop or delete containers on your system.
Therefore we recommend the usage of a socket proxy, which can prohibit certain actions.
A few examples include:
+
- https://github.com/linuxserver/docker-socket-proxy
- https://github.com/Tecnativa/docker-socket-proxy
@@ -178,36 +181,44 @@ See documentation of the respective proxies on how to configure them.
Homarr may behave in unexpected ways when you use proxies.
#### Permissions
+
Homarr needs the following permissions from the Docker API:
+
- Containers/Start
- Containers/Stop
- Containers/Restart
- Containers/Remove
For socket proxies, you will need these permissions:
+
- `CONTAINERS=1`
- `POST=1`
**Caution:** `POST` access is security critical as it provides extensive capabilities to modify your docker environment. Please leave it disabled if you're concerned about this.
As a workaround, you can use [LSIO's socket proxy](https://github.com/linuxserver/docker-socket-proxy) and set the following:
+
- ALLOW_START=1
- ALLOW_STOP=1
- ALLOW_RESTARTS=1
-These will work even with `POST=0`.
+These will work even with `POST=0`.
You lose the ability to remove containers, but start, stop and restarts should work just fine.
#### Connecting to Docker via Socket Proxies
+
To connect to Docker via a socket proxy, you'll need to add these two environment variables in the compose file:
+
- `DOCKER_HOSTNAMES=`
- `DOCKER_PORTS=`
Refer: https://homarr.dev/docs/advanced/environment-variables/
You will also need to add Homarr to the network of your socket proxy. You can do it like this:
+
1. Add the network to your compose file (with appropriate changes):
+
```yaml
networks:
socket-proxy:
@@ -216,6 +227,7 @@ networks:
```
2. Add the network to the homarr service in the compose file:
+
```yaml
networks:
- socket-proxy
diff --git a/apps/docs/docs/integrations/emby/index.mdx b/apps/docs/docs/integrations/emby/index.mdx
index 907d2bd96..2426a1561 100644
--- a/apps/docs/docs/integrations/emby/index.mdx
+++ b/apps/docs/docs/integrations/emby/index.mdx
@@ -1,37 +1,35 @@
---
-title: 'Emby'
+title: "Emby"
description: "Emby is a media server platform that allows you to organize, manage, and stream your personal media collection."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconVideo, IconTicket } from "@tabler/icons-react";
-import { embyIntegration } from '.';
-import { mediaServerWidget } from '@site/docs/widgets/media-server';
-import { mediaReleasesWidget } from '@site/docs/widgets/media-releases';
+import { embyIntegration } from ".";
+import { mediaServerWidget } from "@site/docs/widgets/media-server";
+import { mediaReleasesWidget } from "@site/docs/widgets/media-releases";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/glances/index.mdx b/apps/docs/docs/integrations/glances/index.mdx
index 2639f87e5..bd047e81d 100644
--- a/apps/docs/docs/integrations/glances/index.mdx
+++ b/apps/docs/docs/integrations/glances/index.mdx
@@ -1,37 +1,43 @@
---
-title: 'Glances'
+title: "Glances"
description: "Glances is a cross-platform system monitoring tool."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { glancesIntegration } from '.';
-import { systemResourcesWidget } from '@site/docs/widgets/system-resources';
-import { healthMonitoringWidget } from '@site/docs/widgets/health-monitoring';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { glancesIntegration } from ".";
+import { systemResourcesWidget } from "@site/docs/widgets/system-resources";
+import { healthMonitoringWidget } from "@site/docs/widgets/health-monitoring";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/gluetun/index.mdx b/apps/docs/docs/integrations/gluetun/index.mdx
index 7c936fc1f..dca490781 100644
--- a/apps/docs/docs/integrations/gluetun/index.mdx
+++ b/apps/docs/docs/integrations/gluetun/index.mdx
@@ -1,20 +1,17 @@
---
-title: 'Gluetun'
-description: 'Connect Gluetun to Homarr as a VPN integration.'
+title: "Gluetun"
+description: "Connect Gluetun to Homarr as a VPN integration."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { gluetunIntegration } from '.';
-import { vpnWidget } from '@site/docs/widgets/vpn';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { gluetunIntegration } from ".";
+import { vpnWidget } from "@site/docs/widgets/vpn";
-
+
[Gluetun](https://github.com/qdm12/gluetun) exposes a control server that Homarr can query to report the VPN tunnel
status, DNS status, public IP address, and active provider details in the VPN widget.
@@ -22,9 +19,11 @@ status, DNS status, public IP address, and active provider details in the VPN wi
### Widgets & Capabilities
### Setting up the Gluetun control server
@@ -40,13 +39,20 @@ Refer to the [Gluetun control server documentation](https://github.com/qdm12/glu
Gluetun's control server can be left unprotected or secured with either an API key or HTTP Basic authentication.
Choose the option that matches how your Gluetun instance integration is set up.
-
+
diff --git a/apps/docs/docs/integrations/gotify/index.mdx b/apps/docs/docs/integrations/gotify/index.mdx
index 09c81265b..43cde77b0 100644
--- a/apps/docs/docs/integrations/gotify/index.mdx
+++ b/apps/docs/docs/integrations/gotify/index.mdx
@@ -1,33 +1,39 @@
---
-title: 'Gotify'
+title: "Gotify"
description: "Gotify is a simple, self-hosted server for sending and receiving messages in real time."
hide_title: true
---
-import { notificationsWidget } from '@site/docs/widgets/notifications';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { gotifyIntegration } from '.';
+import { notificationsWidget } from "@site/docs/widgets/notifications";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { gotifyIntegration } from ".";
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/home-assistant/index.mdx b/apps/docs/docs/integrations/home-assistant/index.mdx
index 98ad591d6..65d09d166 100644
--- a/apps/docs/docs/integrations/home-assistant/index.mdx
+++ b/apps/docs/docs/integrations/home-assistant/index.mdx
@@ -1,45 +1,51 @@
---
-title: 'Home Assistant'
+title: "Home Assistant"
description: "Home Assistant is an open-source home automation platform that focuses on privacy and local control, allowing you to automate and control your smart home devices."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconBinaryTree } from "@tabler/icons-react";
-import { homeAssistantIntegration } from '.';
-import { smartHomeEntityStateWidget } from '@site/docs/widgets/smart-home-entity-state';
-import { smartHomeExecuteAutomationWidget } from '@site/docs/widgets/smart-home-execute-automation';
+import { homeAssistantIntegration } from ".";
+import { smartHomeEntityStateWidget } from "@site/docs/widgets/smart-home-entity-state";
+import { smartHomeExecuteAutomationWidget } from "@site/docs/widgets/smart-home-execute-automation";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+ ],
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/ical/index.mdx b/apps/docs/docs/integrations/ical/index.mdx
index 5f5ffc422..475229a34 100644
--- a/apps/docs/docs/integrations/ical/index.mdx
+++ b/apps/docs/docs/integrations/ical/index.mdx
@@ -1,38 +1,44 @@
---
-title: 'iCal'
+title: "iCal"
description: "iCal is a standard for calendar data exchange, allowing users to share and manage calendar events across different platforms."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconZoomQuestion, IconChartBar, IconSearch } from "@tabler/icons-react";
-import { icalIntegration } from '.';
-import { calendarWidget } from '@site/docs/widgets/calendar';
+import { icalIntegration } from ".";
+import { calendarWidget } from "@site/docs/widgets/calendar";
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
+
+
## Using the iCal integration
-The iCal integration allows you to add external calendars to Homarr. Any calendar that provides a public iCal URL can be integrated, such as Google Calendar, Apple Calendar, or Outlook.
\ No newline at end of file
+The iCal integration allows you to add external calendars to Homarr. Any calendar that provides a public iCal URL can be integrated, such as Google Calendar, Apple Calendar, or Outlook.
diff --git a/apps/docs/docs/integrations/immich/index.mdx b/apps/docs/docs/integrations/immich/index.mdx
index a0a351f15..efe4c5106 100644
--- a/apps/docs/docs/integrations/immich/index.mdx
+++ b/apps/docs/docs/integrations/immich/index.mdx
@@ -1,36 +1,29 @@
---
-title: 'Immich'
+title: "Immich"
description: "Self-hosted photo and video management solution"
hide_title: true
---
-import { immichAlbumCarouselWidget } from '../../widgets/immich-album-carousel';import {
- IntegrationHeader
-} from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { immichIntegration } from '.';
-import { immichServerStatsWidget } from '../../widgets/immich-server-stats';
+import { immichAlbumCarouselWidget } from "../../widgets/immich-album-carousel";
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { immichIntegration } from ".";
+import { immichServerStatsWidget } from "../../widgets/immich-server-stats";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
+
asset.statistics,{" "}
server.statistics,{" "}
server.about.
+
),
- 'Create the integration in Homarr and copy the API key to there'
- ]
+'Create the integration in Homarr and copy the API key to there'
+]
}]} />
diff --git a/apps/docs/docs/integrations/jellyfin/index.mdx b/apps/docs/docs/integrations/jellyfin/index.mdx
index 4826386b8..83e23fd45 100644
--- a/apps/docs/docs/integrations/jellyfin/index.mdx
+++ b/apps/docs/docs/integrations/jellyfin/index.mdx
@@ -1,40 +1,39 @@
---
-title: 'Jellyfin'
+title: "Jellyfin"
description: "Jellyfin is a free and open-source media server software that allows you to organize, manage, and share your media files."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconVideo, IconTicket } from "@tabler/icons-react";
-import { jellyfinIntegration } from '.';
-import { mediaServerWidget } from '@site/docs/widgets/media-server';
-import { mediaReleasesWidget } from '@site/docs/widgets/media-releases';
+import { jellyfinIntegration } from ".";
+import { mediaServerWidget } from "@site/docs/widgets/media-server";
+import { mediaReleasesWidget } from "@site/docs/widgets/media-releases";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/jellyseerr/index.mdx b/apps/docs/docs/integrations/jellyseerr/index.mdx
index a85d7bd9f..e752906bb 100644
--- a/apps/docs/docs/integrations/jellyseerr/index.mdx
+++ b/apps/docs/docs/integrations/jellyseerr/index.mdx
@@ -1,45 +1,52 @@
---
-title: 'Jellyseerr'
+title: "Jellyseerr"
description: "Jellyseerr is a self-hosted media request management system that integrates with Jellyfin and other media servers."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconZoomQuestion, IconChartBar, IconSearch } from "@tabler/icons-react";
-import { jellyseerrIntegration } from '.';
-import { mediaRequestListWidget } from '@site/docs/widgets/media-request-list';
-import { mediaRequestStatsWidget } from '@site/docs/widgets/media-request-stats';
+import { jellyseerrIntegration } from ".";
+import { mediaRequestListWidget } from "@site/docs/widgets/media-request-list";
+import { mediaRequestStatsWidget } from "@site/docs/widgets/media-request-stats";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/kubernetes/index.mdx b/apps/docs/docs/integrations/kubernetes/index.mdx
index 899c60931..662874467 100644
--- a/apps/docs/docs/integrations/kubernetes/index.mdx
+++ b/apps/docs/docs/integrations/kubernetes/index.mdx
@@ -1,20 +1,16 @@
---
-title: 'Kubernetes'
+title: "Kubernetes"
description: "Kubernetes is a powerful container orchestration platform that automates the deployment, scaling, and management of containerized applications."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconBrandDocker, IconTool } from "@tabler/icons-react";
-import { kubernetesIntegration } from '.';
+import { kubernetesIntegration } from ".";
-
-
+
Homarr provides a user-friendly Kubernetes dashboard that offers an overview of your cluster’s resources in a clear and structured interface. It allows users to view key Kubernetes objects, including `Pods`, `Services`, `Ingresses`, `Nodes`, `ConfigMaps`, `Secrets`, `Namespaces`, and `Volumes`, as well as basic metrics when the Kubernetes Metrics Server is enabled.
@@ -47,7 +43,7 @@ Enabling RBAC in the Helm chart creates the necessary Kubernetes Role-Based Acce
- Service Account: A dedicated service account for Homarr.
- Role & RoleBinding: Grants permissions within a specific namespace to list, watch, and get pods, services, secrets, configmaps, and more.
- ClusterRole & ClusterRoleBinding: Extends the same permissions cluster-wide for additional flexibility.
-:::
+ :::
### Homarr Dashboard Overview
diff --git a/apps/docs/docs/integrations/lidarr/index.mdx b/apps/docs/docs/integrations/lidarr/index.mdx
index 498503617..ad5916a3f 100644
--- a/apps/docs/docs/integrations/lidarr/index.mdx
+++ b/apps/docs/docs/integrations/lidarr/index.mdx
@@ -1,35 +1,40 @@
---
-title: 'Lidarr'
+title: "Lidarr"
description: "Lidarr is a music management tool that automates the process of downloading, sorting, and renaming music."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconCalendar } from "@tabler/icons-react";
-import { lidarrIntegration } from '.';
-import { calendarWidget } from '@site/docs/widgets/calendar';
+import { lidarrIntegration } from ".";
+import { calendarWidget } from "@site/docs/widgets/calendar";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/navidrome/index.mdx b/apps/docs/docs/integrations/navidrome/index.mdx
index 6a9f4059e..d5b1993ad 100644
--- a/apps/docs/docs/integrations/navidrome/index.mdx
+++ b/apps/docs/docs/integrations/navidrome/index.mdx
@@ -1,41 +1,39 @@
---
-title: 'Navidrome'
+title: "Navidrome"
description: "Navidrome is a self-hosted music server and streamer compatible with the Subsonic API."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { navidromeIntegration } from '.';
-import { audioStatsWidget } from '@site/docs/widgets/audio-stats';
-import { mediaServerWidget } from '@site/docs/widgets/media-server';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { navidromeIntegration } from ".";
+import { audioStatsWidget } from "@site/docs/widgets/audio-stats";
+import { mediaServerWidget } from "@site/docs/widgets/media-server";
-
-
+
Navidrome provides music library statistics including artist, album, and song counts, as well as now playing audio streams via the Subsonic API.
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/nextcloud/index.mdx b/apps/docs/docs/integrations/nextcloud/index.mdx
index ba5ab8ee9..f7d7e05fa 100644
--- a/apps/docs/docs/integrations/nextcloud/index.mdx
+++ b/apps/docs/docs/integrations/nextcloud/index.mdx
@@ -1,40 +1,43 @@
---
-title: 'Nextcloud'
+title: "Nextcloud"
description: "Nextcloud is a self-hosted productivity platform that provides file storage, collaboration tools, and more."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconCalendar } from "@tabler/icons-react";
-import Alert from '@theme/Admonition';
-import { nextcloudIntegration } from '.';
-import { calendarWidget } from '@site/docs/widgets/calendar';
-import { notificationsWidget } from '@site/docs/widgets/notifications';
+import Alert from "@theme/Admonition";
+import { nextcloudIntegration } from ".";
+import { calendarWidget } from "@site/docs/widgets/calendar";
+import { notificationsWidget } from "@site/docs/widgets/notifications";
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
- Users who use 2FA will have to use Nextcloud's App Password tool to grant Homarr access, as 2FA breaks the traditional process. For more information, see [Nextcloud's Documentation](https://docs.nextcloud.com/server/stable/admin_manual/configuration_user/authentication.html#app-passwords)
- ,
-}]} />
+
+
+ Users who use 2FA will have to use Nextcloud's App Password tool to grant Homarr access, as 2FA breaks the
+ traditional process. For more information, see [Nextcloud's
+ Documentation](https://docs.nextcloud.com/server/stable/admin_manual/configuration_user/authentication.html#app-passwords)
+
+ ),
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/ntfy/index.mdx b/apps/docs/docs/integrations/ntfy/index.mdx
index 4b21d16a4..a132de923 100644
--- a/apps/docs/docs/integrations/ntfy/index.mdx
+++ b/apps/docs/docs/integrations/ntfy/index.mdx
@@ -1,37 +1,44 @@
---
-title: 'Ntfy'
+title: "Ntfy"
description: "Ntfy is a simple, open-source notification service that allows you to send and receive notifications across devices."
hide_title: true
---
-import { notificationsWidget } from '@site/docs/widgets/notifications';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
+import { notificationsWidget } from "@site/docs/widgets/notifications";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
import { IconMessage } from "@tabler/icons-react";
-import { ntfyIntegration } from '.';
+import { ntfyIntegration } from ".";
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/nzbget/index.mdx b/apps/docs/docs/integrations/nzbget/index.mdx
index 3479f317a..1b7c23a1f 100644
--- a/apps/docs/docs/integrations/nzbget/index.mdx
+++ b/apps/docs/docs/integrations/nzbget/index.mdx
@@ -1,36 +1,41 @@
---
-title: 'NZBGet'
+title: "NZBGet"
description: "NZBGet is a lightweight and efficient Usenet downloader."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconDownload } from "@tabler/icons-react";
-import { nzbgetIntegration } from '.';
-import Alert from '@theme/Admonition';
-import { downloadsWidget } from '@site/docs/widgets/downloads';
+import { nzbgetIntegration } from ".";
+import Alert from "@theme/Admonition";
+import { downloadsWidget } from "@site/docs/widgets/downloads";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/open-media-vault/index.mdx b/apps/docs/docs/integrations/open-media-vault/index.mdx
index ac0a42290..b934bf797 100644
--- a/apps/docs/docs/integrations/open-media-vault/index.mdx
+++ b/apps/docs/docs/integrations/open-media-vault/index.mdx
@@ -1,42 +1,52 @@
---
-title: 'Open Media Vault'
+title: "Open Media Vault"
description: "Open Media Vault is a free network-attached storage server based on the Debian operating system."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconHeartRateMonitor } from "@tabler/icons-react";
-import { openMediaVaultIntegration } from '.';
-import Alert from '@theme/Admonition'
-import { healthMonitoringWidget } from '@site/docs/widgets/health-monitoring';
-import { systemResourcesWidget } from '@site/docs/widgets/system-resources';
+import { openMediaVaultIntegration } from ".";
+import Alert from "@theme/Admonition";
+import { healthMonitoringWidget } from "@site/docs/widgets/health-monitoring";
+import { systemResourcesWidget } from "@site/docs/widgets/system-resources";
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
- The user used here must have administrative permissions or Homarr won't be able to read the system performance and resource data.
-
-}]} />
\ No newline at end of file
+
+
+ The user used here must have administrative permissions or Homarr won't be able to read the system performance
+ and resource data.
+
+ ),
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/opnsense/index.mdx b/apps/docs/docs/integrations/opnsense/index.mdx
index 37e9a78c6..9a3a2b1ee 100644
--- a/apps/docs/docs/integrations/opnsense/index.mdx
+++ b/apps/docs/docs/integrations/opnsense/index.mdx
@@ -1,65 +1,75 @@
---
-title: 'OPNsense'
+title: "OPNsense"
description: "OPNsense is an open-source, easy-to-use, and easy-to-build firewall and routing platform."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconHeartRateMonitor } from "@tabler/icons-react";
-import { opnsenseIntegration } from '.';
-import Alert from '@theme/Admonition'
-import { firewallWidget } from '../../widgets/firewall';
+import { opnsenseIntegration } from ".";
+import Alert from "@theme/Admonition";
+import { firewallWidget } from "../../widgets/firewall";
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
- This integration does not require full API access to the firewall. The following guide outlines the process for creating a restricted OPNsense user with a scrambled password, designed exclusively for API access. This ensures minimal permissions and prevents local login capability.
- ,
- steps: [
+
+
+ This integration does not require full API access to the firewall. The following guide outlines the process
+ for creating a restricted OPNsense user with a scrambled password, designed exclusively for API access. This
+ ensures minimal permissions and prevents local login capability.
+
+ ),
+ steps: [
"Log in to the OPNsense web interface as an administrator",
"Navigate to System → Access → Users",
"Click the + (Add) button to create a new user",
<>
- Fill in the user details:
-
-
Username: Enter a unique name
-
Password: Leave empty
-
Scrambled Password: Enable this option
-
Full name: (Optional) Add a descriptive name
-
+ Fill in the user details:
+
+
Username: Enter a unique name
+
Password: Leave empty
+
Scrambled Password: Enable this option
+
Full name: (Optional) Add a descriptive name
+
>,
<>
- In Privileges section → Select the option below:
-
-
Lobby: Dashboard
-
Reporting: Traffic
-
+ In Privileges section → Select the option below:
+
+
Lobby: Dashboard
+
Reporting: Traffic
+
>,
"Click Save",
"Return to the Users list",
"Find the user and click the ticket icon in the Commands column",
"Click Yes in the popup to confirm",
"A txt file will download automatically, containing: Key and Secret",
- "Save this file securely—this information will only be shown once"
- ],
-}]} />
\ No newline at end of file
+ "Save this file securely—this information will only be shown once",
+ ],
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/overseerr/index.mdx b/apps/docs/docs/integrations/overseerr/index.mdx
index ce9e0a3e5..2dd2d7c8a 100644
--- a/apps/docs/docs/integrations/overseerr/index.mdx
+++ b/apps/docs/docs/integrations/overseerr/index.mdx
@@ -1,45 +1,52 @@
---
-title: 'Overseerr'
+title: "Overseerr"
description: "Overseerr is a self-hosted media request management system that integrates with Plex."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconZoomQuestion, IconChartBar, IconSearch } from "@tabler/icons-react";
-import { overseerrIntegration } from '.';
-import { mediaRequestListWidget } from '@site/docs/widgets/media-request-list';
-import { mediaRequestStatsWidget } from '@site/docs/widgets/media-request-stats';
+import { overseerrIntegration } from ".";
+import { mediaRequestListWidget } from "@site/docs/widgets/media-request-list";
+import { mediaRequestStatsWidget } from "@site/docs/widgets/media-request-stats";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/paperless-ngx/index.mdx b/apps/docs/docs/integrations/paperless-ngx/index.mdx
index e08840110..175fdb15a 100644
--- a/apps/docs/docs/integrations/paperless-ngx/index.mdx
+++ b/apps/docs/docs/integrations/paperless-ngx/index.mdx
@@ -1,41 +1,40 @@
---
-title: 'Paperless-ngx'
+title: "Paperless-ngx"
description: "Paperless-ngx is a document management system that transforms your physical documents into a searchable online archive."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { paperlessNgxIntegration } from '.';
-import { paperlessNgxWidget } from '@site/docs/widgets/paperless-ngx';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { paperlessNgxIntegration } from ".";
+import { paperlessNgxWidget } from "@site/docs/widgets/paperless-ngx";
-
-
+
Paperless-ngx provides document statistics including total documents, inbox count, correspondents, tags, and document types.
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/patchmon/index.mdx b/apps/docs/docs/integrations/patchmon/index.mdx
index 95b827df0..b60c20962 100644
--- a/apps/docs/docs/integrations/patchmon/index.mdx
+++ b/apps/docs/docs/integrations/patchmon/index.mdx
@@ -1,43 +1,42 @@
---
-title: 'PatchMon'
+title: "PatchMon"
description: "PatchMon is a patch management and monitoring solution for Linux hosts."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { patchmonIntegration } from '.';
-import { patchmonWidget } from '@site/docs/widgets/patchmon';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { patchmonIntegration } from ".";
+import { patchmonWidget } from "@site/docs/widgets/patchmon";
-
-
+
PatchMon provides host and package update statistics including total hosts, hosts needing updates, and security update counts.
The PatchMon widget requires users to be **signed in to Homarr**; see the [PatchMon widget documentation](/docs/widgets/patchmon#authentication) for details on authentication and public boards.
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/peanut/index.mdx b/apps/docs/docs/integrations/peanut/index.mdx
index 751200bed..7eb7b845b 100644
--- a/apps/docs/docs/integrations/peanut/index.mdx
+++ b/apps/docs/docs/integrations/peanut/index.mdx
@@ -1,48 +1,53 @@
---
-title: 'PeaNUT'
-description: 'A tiny dashboard for Network UPS Tools (NUT) servers.'
+title: "PeaNUT"
+description: "A tiny dashboard for Network UPS Tools (NUT) servers."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { peaNutIntegration } from '.';
-import { upsWidget } from '@site/docs/widgets/ups';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { peaNutIntegration } from ".";
+import { upsWidget } from "@site/docs/widgets/ups";
-
+
[PeaNUT](https://github.com/Brandawg93/PeaNUT) exposes the data of your [Network UPS Tools](https://networkupstools.org/) servers
over a REST API, which Homarr reads to display the status of your UPS devices.
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
+
PeaNUT secures its API with HTTP Basic authentication when the `WEB_USERNAME` and `WEB_PASSWORD` environment variables
are set on the PeaNUT instance. Provide the same credentials here. If the instance runs with `AUTH_DISABLED=true`, no
secrets are required.
-
+
diff --git a/apps/docs/docs/integrations/pi-hole/index.mdx b/apps/docs/docs/integrations/pi-hole/index.mdx
index ca2b400e7..d724b026d 100644
--- a/apps/docs/docs/integrations/pi-hole/index.mdx
+++ b/apps/docs/docs/integrations/pi-hole/index.mdx
@@ -1,47 +1,54 @@
---
-title: 'Pi-hole'
+title: "Pi-hole"
description: "Pi-hole is a network-wide ad blocker that acts as a DNS sinkhole, blocking unwanted content and improving your browsing experience."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconAd, IconDeviceGamepad } from "@tabler/icons-react";
-import Alert from '@theme/Admonition';
-import { dnsHoleSummaryWidget } from '@site/docs/widgets/dns-hole-summary';
-import { dnsHoleControlsWidget } from '@site/docs/widgets/dns-hole-controls';
-import { piHoleIntegration } from '.';
+import Alert from "@theme/Admonition";
+import { dnsHoleSummaryWidget } from "@site/docs/widgets/dns-hole-summary";
+import { dnsHoleControlsWidget } from "@site/docs/widgets/dns-hole-controls";
+import { piHoleIntegration } from ".";
+
-
-
-
- Homarr supports both Pi-hole v5 and v6. It automatically detects the version and uses the correct API.
+
+ Homarr supports both Pi-hole v5 and v6. It automatically detects the version and uses the correct API.
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/plex/index.mdx b/apps/docs/docs/integrations/plex/index.mdx
index b6394b918..b6e35a6e3 100644
--- a/apps/docs/docs/integrations/plex/index.mdx
+++ b/apps/docs/docs/integrations/plex/index.mdx
@@ -1,41 +1,47 @@
---
-title: 'Plex'
+title: "Plex"
description: "Plex is a media server platform that allows you to organize, manage, and stream your personal media collection."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconVideo, IconTicket } from "@tabler/icons-react";
-import { plexIntegration } from '.';
-import { mediaServerWidget } from '@site/docs/widgets/media-server';
-import { mediaReleasesWidget } from '@site/docs/widgets/media-releases';
+import { plexIntegration } from ".";
+import { mediaServerWidget } from "@site/docs/widgets/media-server";
+import { mediaReleasesWidget } from "@site/docs/widgets/media-releases";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
- Follow the official Plex documentation to find your authentication token.
-
- ]
-}]} />
\ No newline at end of file
+ Follow the official{" "}
+
+ Plex documentation
+ {" "}
+ to find your authentication token.
+ ,
+ ],
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/prowlarr/index.mdx b/apps/docs/docs/integrations/prowlarr/index.mdx
index 0c5561f91..b3624a97e 100644
--- a/apps/docs/docs/integrations/prowlarr/index.mdx
+++ b/apps/docs/docs/integrations/prowlarr/index.mdx
@@ -1,32 +1,34 @@
---
-title: 'Prowlarr'
+title: "Prowlarr"
description: "Prowlarr is an indexer manager for Usenet and BitTorrent, providing a unified interface to manage your indexers."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconHeartRateMonitor } from "@tabler/icons-react";
-import { prowlarrIntegration } from '.';
-import { indexerManagerWidget } from '@site/docs/widgets/indexer-manager';
+import { prowlarrIntegration } from ".";
+import { indexerManagerWidget } from "@site/docs/widgets/indexer-manager";
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/proxmox/index.mdx b/apps/docs/docs/integrations/proxmox/index.mdx
index 4ea8eda98..f038dd007 100644
--- a/apps/docs/docs/integrations/proxmox/index.mdx
+++ b/apps/docs/docs/integrations/proxmox/index.mdx
@@ -1,103 +1,107 @@
---
-title: 'Proxmox'
+title: "Proxmox"
description: "Proxmox is a powerful open-source virtualization platform that allows you to run virtual machines and containers."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconHeartRateMonitor } from "@tabler/icons-react";
-import { proxmoxIntegration } from '.';
-import Alert from '@theme/Admonition'
-import { healthMonitoringWidget } from '@site/docs/widgets/health-monitoring';
-import { systemResourcesWidget } from '@site/docs/widgets/system-resources';
+import { proxmoxIntegration } from ".";
+import Alert from "@theme/Admonition";
+import { healthMonitoringWidget } from "@site/docs/widgets/health-monitoring";
+import { systemResourcesWidget } from "@site/docs/widgets/system-resources";
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
- The Proxmox API may not work with Linux PAM standard authentication. It is strongly recommended to use the Proxmox VE authentication server realm when creating your API user.
- ,
- credentials: ['username', 'tokenId', 'realm', 'apiKey'],
- steps: [
+
+
+ The Proxmox API may not work with Linux PAM standard authentication. It is strongly recommended to use the
+ Proxmox VE authentication server realm when creating your API user.
+
+ ),
+ credentials: ["username", "tokenId", "realm", "apiKey"],
+ steps: [
"Navigate to the Proxmox portal, click on Datacenter",
"Expand Permissions, click on Groups",
"Click the Create button",
"Name the group something informative, like api-users",
"Click on the Permissions 'folder'",
<>
- Click Add {"->"} Group Permission
-
-
Path: /
-
Group: group from Step 4 above
-
Role: PVEAuditor
-
Propagate: Checked
-
+ Click Add {"->"} Group Permission
+
+
Path: /
+
Group: group from Step 4 above
+
Role: PVEAuditor
+
Propagate: Checked
+
>,
"Expand Permissions, click on Users",
<>
- Click the Add button
-
-
User name: something informative like api
-
Realm: Proxmox VE authentication server
-
Password: create a secure password for the user
-
Confirm Password: re-enter the password
-
Group: group from Step 4 above
-
+ Click the Add button
+
+
User name: something informative like api
+
Realm: Proxmox VE authentication server
+
Password: create a secure password for the user
+
Confirm Password: re-enter the password
+
Group: group from Step 4 above
+
>,
"Expand Permissions, click on API Tokens",
<>
- Click the Add button
-
-
User: user from Step 8 above
-
Token ID: something informative like the application or purpose like homarr
-
Privilege Separation: unchecked
-
+ Click the Add button
+
+
User: user from Step 8 above
+
Token ID: something informative like the application or purpose like homarr
+
Privilege Separation: unchecked
+
>,
"Copy the Secret that is shown in Step 10 because it is only shown once",
"Go back to the 'Permissions' menu",
<>
- Click Add {"->"} API Token Permission
-
-
Path: /
-
API Token: select the API token created in Step 10
-
Role: PVE Auditor
-
Propagate: Checked
-
+ Click Add {"->"} API Token Permission
+
+
Path: /
+
API Token: select the API token created in Step 10
+
Role: PVE Auditor
+
Propagate: Checked
+
>,
- ],
- body: <>
-
- Sometimes you'll also find the api@pve!homarr format,
- within Homarr you'll have to split it up into:
-
-
+ ],
+ body: (
+ <>
+
+ Sometimes you'll also find the api@pve!homarr format, within Homarr you'll have to split it up
+ into:
+
+
- Username: api
+ Username: api
- Realm: pve
+ Realm: pve
- Token ID: homarr
+ Token ID: homarr
-
- >
-}]} />
\ No newline at end of file
+
+ >
+ ),
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/q-bittorent/index.mdx b/apps/docs/docs/integrations/q-bittorent/index.mdx
index ae85fc0ea..0bf2474ec 100644
--- a/apps/docs/docs/integrations/q-bittorent/index.mdx
+++ b/apps/docs/docs/integrations/q-bittorent/index.mdx
@@ -1,37 +1,39 @@
---
-title: 'qBittorrent'
+title: "qBittorrent"
description: "qBittorrent is a free and open-source BitTorrent client."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconDownload } from "@tabler/icons-react";
-import { qBittorentIntegration } from '.';
-import Alert from '@theme/Admonition';
-import { downloadsWidget } from '@site/docs/widgets/downloads';
+import { qBittorentIntegration } from ".";
+import Alert from "@theme/Admonition";
+import { downloadsWidget } from "@site/docs/widgets/downloads";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/radarr/index.mdx b/apps/docs/docs/integrations/radarr/index.mdx
index 286e1b0b7..09548cab7 100644
--- a/apps/docs/docs/integrations/radarr/index.mdx
+++ b/apps/docs/docs/integrations/radarr/index.mdx
@@ -1,33 +1,34 @@
---
-title: 'Radarr'
+title: "Radarr"
description: "Radarr is a movie management tool that automates the process of downloading, sorting, and renaming movies."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconCalendar } from "@tabler/icons-react";
-import { radarrIntegration } from '.';
-import { calendarWidget } from '@site/docs/widgets/calendar';
+import { radarrIntegration } from ".";
+import { calendarWidget } from "@site/docs/widgets/calendar";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/readarr/index.mdx b/apps/docs/docs/integrations/readarr/index.mdx
index a2c153a91..aec31fe1a 100644
--- a/apps/docs/docs/integrations/readarr/index.mdx
+++ b/apps/docs/docs/integrations/readarr/index.mdx
@@ -1,33 +1,34 @@
---
-title: 'Readarr'
+title: "Readarr"
description: "Readarr is a book management tool that automates the process of downloading, sorting, and renaming books."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconCalendar } from "@tabler/icons-react";
-import { readarrIntegration } from '.';
-import { calendarWidget } from '@site/docs/widgets/calendar';
+import { readarrIntegration } from ".";
+import { calendarWidget } from "@site/docs/widgets/calendar";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/sabnzbd/index.mdx b/apps/docs/docs/integrations/sabnzbd/index.mdx
index 5de72d8c3..8c91c6fef 100644
--- a/apps/docs/docs/integrations/sabnzbd/index.mdx
+++ b/apps/docs/docs/integrations/sabnzbd/index.mdx
@@ -1,34 +1,35 @@
---
-title: 'SABnzbd'
+title: "SABnzbd"
description: "SABnzbd is an Open Source Binary Newsreader."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconDownload } from "@tabler/icons-react";
-import { sabnzbdIntegration } from '.';
-import Alert from '@theme/Admonition';
-import { downloadsWidget } from '@site/docs/widgets/downloads';
+import { sabnzbdIntegration } from ".";
+import Alert from "@theme/Admonition";
+import { downloadsWidget } from "@site/docs/widgets/downloads";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/seerr/index.mdx b/apps/docs/docs/integrations/seerr/index.mdx
index 81b7e0fb1..a1e30798e 100644
--- a/apps/docs/docs/integrations/seerr/index.mdx
+++ b/apps/docs/docs/integrations/seerr/index.mdx
@@ -1,45 +1,52 @@
---
-title: 'Seerr'
+title: "Seerr"
description: "Seerr is a self-hosted media request management system that integrates with various media servers."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconZoomQuestion, IconChartBar, IconSearch } from "@tabler/icons-react";
-import { seerrIntegration } from '.';
-import { mediaRequestListWidget } from '@site/docs/widgets/media-request-list';
-import { mediaRequestStatsWidget } from '@site/docs/widgets/media-request-stats';
+import { seerrIntegration } from ".";
+import { mediaRequestListWidget } from "@site/docs/widgets/media-request-list";
+import { mediaRequestStatsWidget } from "@site/docs/widgets/media-request-stats";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/slskd/index.mdx b/apps/docs/docs/integrations/slskd/index.mdx
index 70674cb04..705ab43c0 100644
--- a/apps/docs/docs/integrations/slskd/index.mdx
+++ b/apps/docs/docs/integrations/slskd/index.mdx
@@ -1,34 +1,39 @@
---
-title: 'Slskd'
+title: "Slskd"
description: "A modern client-server application for the Soulseek file sharing network."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { slskdIntegration } from '.';
-import { downloadsWidget } from '@site/docs/widgets/downloads';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { slskdIntegration } from ".";
+import { downloadsWidget } from "@site/docs/widgets/downloads";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/sonarr/index.mdx b/apps/docs/docs/integrations/sonarr/index.mdx
index 7dc3c7a64..c65d3ce3d 100644
--- a/apps/docs/docs/integrations/sonarr/index.mdx
+++ b/apps/docs/docs/integrations/sonarr/index.mdx
@@ -1,33 +1,34 @@
---
-title: 'Sonarr'
+title: "Sonarr"
description: "Sonarr is a TV series management tool that automates the process of downloading, sorting, and renaming episodes."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconCalendar } from "@tabler/icons-react";
-import { sonarrIntegration } from '.';
-import { calendarWidget } from '@site/docs/widgets/calendar';
+import { sonarrIntegration } from ".";
+import { calendarWidget } from "@site/docs/widgets/calendar";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/speedtest-tracker/index.mdx b/apps/docs/docs/integrations/speedtest-tracker/index.mdx
index 2041dad5c..146a09eca 100644
--- a/apps/docs/docs/integrations/speedtest-tracker/index.mdx
+++ b/apps/docs/docs/integrations/speedtest-tracker/index.mdx
@@ -1,34 +1,39 @@
---
-title: 'Speedtest Tracker'
-description: 'Speedtest Tracker is a self-hosted internet performance tracking application.'
+title: "Speedtest Tracker"
+description: "Speedtest Tracker is a self-hosted internet performance tracking application."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { speedtestTrackerIntegration } from '.';
-import { speedtestTrackerWidget } from '@site/docs/widgets/speedtest-tracker';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { speedtestTrackerIntegration } from ".";
+import { speedtestTrackerWidget } from "@site/docs/widgets/speedtest-tracker";
-
-
+
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/synology/index.mdx b/apps/docs/docs/integrations/synology/index.mdx
index 4d836513e..c3f50fc3f 100644
--- a/apps/docs/docs/integrations/synology/index.mdx
+++ b/apps/docs/docs/integrations/synology/index.mdx
@@ -19,11 +19,7 @@ import Alert from "@theme/Admonition";
### Widgets & Capabilities
### Supported metrics
diff --git a/apps/docs/docs/integrations/tdarr/index.mdx b/apps/docs/docs/integrations/tdarr/index.mdx
index 89a0730d2..47956343d 100644
--- a/apps/docs/docs/integrations/tdarr/index.mdx
+++ b/apps/docs/docs/integrations/tdarr/index.mdx
@@ -1,36 +1,38 @@
---
-title: 'Tdarr'
+title: "Tdarr"
description: "Tdarr is a distributed media transcoding system that allows you to process and optimize your media files across multiple nodes."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconTransform } from "@tabler/icons-react";
-import { tdarrIntegration } from '.';
-import { mediaTranscodingWidget } from '@site/docs/widgets/media-transcoding';
+import { tdarrIntegration } from ".";
+import { mediaTranscodingWidget } from "@site/docs/widgets/media-transcoding";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/technitium-dns/index.mdx b/apps/docs/docs/integrations/technitium-dns/index.mdx
index 3ca8bda7c..3bf39d820 100644
--- a/apps/docs/docs/integrations/technitium-dns/index.mdx
+++ b/apps/docs/docs/integrations/technitium-dns/index.mdx
@@ -1,62 +1,72 @@
---
-title: 'Technitium DNS'
+title: "Technitium DNS"
description: "Technitium DNS Server is a self-hosted DNS server with ad-blocking and network-wide filtering capabilities."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import Alert from '@theme/Admonition';
-import { technitiumDnsIntegration } from '.';
-import { dnsHoleSummaryWidget } from '@site/docs/widgets/dns-hole-summary';
-import { dnsHoleControlsWidget } from '@site/docs/widgets/dns-hole-controls';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import Alert from "@theme/Admonition";
+import { technitiumDnsIntegration } from ".";
+import { dnsHoleSummaryWidget } from "@site/docs/widgets/dns-hole-summary";
+import { dnsHoleControlsWidget } from "@site/docs/widgets/dns-hole-controls";
-
+
-
- Homarr supports Technitium DNS v11 and above. The correct API version is detected automatically - no manual configuration is required.
+
+ Homarr supports Technitium DNS v11 and above. The correct API version is detected automatically - no manual
+ configuration is required.
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
- The **API token** option is recommended. API tokens are non-expiring and do not require storing your admin password in Homarr.
- Username and password authentication uses a session token that expires after inactivity, triggering a silent re-login on the next request.
+
+ The **API token** option is recommended. API tokens are non-expiring and do not require storing your admin password in
+ Homarr. Username and password authentication uses a session token that expires after inactivity, triggering a silent
+ re-login on the next request.
-
- The **DNS Hole Controls** widget (enabling, disabling, and timed disabling of blocking) requires the token to have settings-level permissions.
- The **DNS Hole Summary** widget (stats only) works with any valid token.
- If Homarr cannot read the blocking status due to insufficient permissions, the status indicator will be hidden but statistics will still be shown.
+
+ The **DNS Hole Controls** widget (enabling, disabling, and timed disabling of blocking) requires the token to have
+ settings-level permissions. The **DNS Hole Summary** widget (stats only) works with any valid token. If Homarr cannot
+ read the blocking status due to insufficient permissions, the status indicator will be hidden but statistics will
+ still be shown.
-
+ 'Click on your username in the top-right corner and select "Create API Token".',
+ 'Give the token a name (e.g. "Homarr") and click "Create Token".',
+ "Copy the generated token and paste it into the API Key field in Homarr.",
+ ],
+ },
+ {
+ credentials: ["username", "password"],
+ steps: [],
+ body: "Enter your Technitium DNS admin credentials. Homarr will exchange them for a session token and transparently re-authenticate when the session expires.",
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/tracearr/index.mdx b/apps/docs/docs/integrations/tracearr/index.mdx
index dc4dde8e0..03954a1cc 100644
--- a/apps/docs/docs/integrations/tracearr/index.mdx
+++ b/apps/docs/docs/integrations/tracearr/index.mdx
@@ -1,38 +1,37 @@
---
-title: 'Tracearr'
+title: "Tracearr"
description: "Tracearr is a comprehensive media monitoring solution that tracks streams, user activity, and policy violations in your media server (including Plex, Jellyfin, Emby)."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
-import { tracearrIntegration } from '.';
-import { tracearrWidget } from '@site/docs/widgets/tracearr';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { tracearrIntegration } from ".";
+import { tracearrWidget } from "@site/docs/widgets/tracearr";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
- In Tracearr, go to Settings → API and generate or copy your API key.
-
- ]
-}]} />
+ In Tracearr, go to Settings → API and generate or copy your API key.
+ ,
+ ],
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/transmission/index.mdx b/apps/docs/docs/integrations/transmission/index.mdx
index da0c0209e..c17dbe862 100644
--- a/apps/docs/docs/integrations/transmission/index.mdx
+++ b/apps/docs/docs/integrations/transmission/index.mdx
@@ -1,34 +1,35 @@
---
-title: 'Transmission'
+title: "Transmission"
description: "A Fast, Easy and Free Bittorrent Client."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconDownload } from "@tabler/icons-react";
-import { transmissionIntegration } from '.';
-import Alert from '@theme/Admonition';
-import { downloadsWidget } from '@site/docs/widgets/downloads';
+import { transmissionIntegration } from ".";
+import Alert from "@theme/Admonition";
+import { downloadsWidget } from "@site/docs/widgets/downloads";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/truenas/index.mdx b/apps/docs/docs/integrations/truenas/index.mdx
index 5d29a7019..3ec09bbcf 100644
--- a/apps/docs/docs/integrations/truenas/index.mdx
+++ b/apps/docs/docs/integrations/truenas/index.mdx
@@ -1,61 +1,67 @@
---
-title: 'TrueNAS'
+title: "TrueNAS"
description: "Enterprise network-attached storage for your home, office, and cloud."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { truenasIntegration } from '.';
-import { systemResourcesWidget } from '@site/docs/widgets/system-resources';
-import { healthMonitoringWidget } from '@site/docs/widgets/health-monitoring';
-import Alert from '@theme/Admonition';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { truenasIntegration } from ".";
+import { systemResourcesWidget } from "@site/docs/widgets/system-resources";
+import { healthMonitoringWidget } from "@site/docs/widgets/health-monitoring";
+import Alert from "@theme/Admonition";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
- TrueNAS only serves its API over HTTPS, so enter the https:// URL of your TrueNAS web interface. If it uses a self-signed certificate (the default), add it under Management > Tools > Certificates so Homarr can establish the connection.
+ TrueNAS only serves its API over HTTPS, so enter the https:// URL of your TrueNAS web interface. If it
+ uses a self-signed certificate (the default), add it under{" "}
+ Management > Tools > Certificates so Homarr can establish the
+ connection.
### Secrets
-
- API key authentication is the recommended method. It works across current TrueNAS versions and does not depend on user group membership.
- ,
- steps: [
- "In the TrueNAS web interface, click your user avatar in the top-right corner and select \"API Keys\".",
- "Click \"Add\", give the key a name such as \"Homarr\", and confirm.",
- "Copy the generated key immediately - TrueNAS only shows it once - and paste it as the API Key when creating the integration in Homarr.",
- ],
+ credentials: ["apiKey"],
+ header: (
+
+ API key authentication is the recommended method. It works across current TrueNAS versions and does not depend
+ on user group membership.
+
+ ),
+ steps: [
+ 'In the TrueNAS web interface, click your user avatar in the top-right corner and select "API Keys".',
+ 'Click "Add", give the key a name such as "Homarr", and confirm.',
+ "Copy the generated key immediately - TrueNAS only shows it once - and paste it as the API Key when creating the integration in Homarr.",
+ ],
},
{
- credentials: ['username', 'password'],
- steps: [
- "In the TrueNAS web interface, go to \"Credentials\" > \"Users\" > \"Add\" and create a user with a secure password.",
- "Grant the user administrative access so Homarr can read system, pool, and reporting data.",
- "Save the user and create the integration in Homarr with these credentials.",
- ],
- body:
- The account must have administrative API access. The auxiliary_administrator group was removed in TrueNAS 25.10; on that version or newer, use API key authentication instead, or assign the user a full-admin role.
- ,
+ credentials: ["username", "password"],
+ steps: [
+ 'In the TrueNAS web interface, go to "Credentials" > "Users" > "Add" and create a user with a secure password.',
+ "Grant the user administrative access so Homarr can read system, pool, and reporting data.",
+ "Save the user and create the integration in Homarr with these credentials.",
+ ],
+ body: (
+
+ The account must have administrative API access. The auxiliary_administrator group was removed in
+ TrueNAS 25.10; on that version or newer, use API key authentication instead, or assign the user a full-admin
+ role.
+
+ ),
},
-]} />
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/umami/index.mdx b/apps/docs/docs/integrations/umami/index.mdx
index 8aeb3c194..6aa5ddced 100644
--- a/apps/docs/docs/integrations/umami/index.mdx
+++ b/apps/docs/docs/integrations/umami/index.mdx
@@ -1,53 +1,52 @@
---
-title: 'Umami'
-description: 'Privacy-focused, open-source web analytics'
+title: "Umami"
+description: "Privacy-focused, open-source web analytics"
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { umamiIntegration } from '.';
-import { umamiWidget } from '../../widgets/umami';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { umamiIntegration } from ".";
+import { umamiWidget } from "../../widgets/umami";
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
:::tip URL configuration
+
- **Umami Cloud:** Use `https://api.umami.is/v1`
- **Self-hosted:** Use `http://your-umami-host:3000/api`
-:::
+ :::
### Secrets
-
+
+
diff --git a/apps/docs/docs/integrations/unifi-controller/index.mdx b/apps/docs/docs/integrations/unifi-controller/index.mdx
index 134ce91b8..f25cecaac 100644
--- a/apps/docs/docs/integrations/unifi-controller/index.mdx
+++ b/apps/docs/docs/integrations/unifi-controller/index.mdx
@@ -1,40 +1,40 @@
---
-title: 'Unifi Controller'
+title: "Unifi Controller"
description: "Unifi Controller is a network management platform for Unifi devices."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import {IntegrationSecrets} from '@site/src/components/integrations/secrets';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
import { IconTopologyFull } from "@tabler/icons-react";
-import { unifiControllerIntegration } from '.';
-import { networkControllerSummaryWidget } from '@site/docs/widgets/network-controller-summary';
-import { networkControllerStatusWidget } from '@site/docs/widgets/network-controller-status';
+import { unifiControllerIntegration } from ".";
+import { networkControllerSummaryWidget } from "@site/docs/widgets/network-controller-summary";
+import { networkControllerStatusWidget } from "@site/docs/widgets/network-controller-status";
-
-
+
This integration is using the [node-unifi](https://www.npmjs.com/package/node-unifi) library under the hood to connect to the Unifi Controller.
Therfore it should support CloudKey Gen1, CloudKey Gen2, UnifiOS-based UDM-Pro controllers as well as self-hosted UniFi controllers.
### Widgets & Capabilities
+
### Adding the integration
+
### Secrets
-
\ No newline at end of file
+
+
diff --git a/apps/docs/docs/integrations/unraid/index.mdx b/apps/docs/docs/integrations/unraid/index.mdx
index bd1546a78..a629fe844 100644
--- a/apps/docs/docs/integrations/unraid/index.mdx
+++ b/apps/docs/docs/integrations/unraid/index.mdx
@@ -1,59 +1,71 @@
---
-title: 'Unraid'
+title: "Unraid"
description: "Versatile operating system that lets you run applications, virtual machines, and storage devices on your server"
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { unraidIntegration } from '.';
-import { systemResourcesWidget } from '@site/docs/widgets/system-resources';
-import { healthMonitoringWidget } from '@site/docs/widgets/health-monitoring';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { unraidIntegration } from ".";
+import { systemResourcesWidget } from "@site/docs/widgets/system-resources";
+import { healthMonitoringWidget } from "@site/docs/widgets/health-monitoring";
import imgManagement from "./imgs/unraid-management.png";
import imgCreateApiKey from "./imgs/unraid-create-api-key.png";
import imgCopyApiKey from "./imgs/copy-api-key.png";
import imgSelectApikeyPreset from "./imgs/unraid-select-api-key-preset.png";
-
-
+
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-Are you using Unraid pre-7.2? If yes, please first follow the steps here to install the Unraid API.,
- <>
- Open the settings and open the management access
-
- >,
- <>
- Click on the "API Keys" tab at the top and click on the "create API key" button. Choose "Create new"
-
- >,
- <>
- Enter a suitable name for your API key (e.g. Homarr), select the "Viewer" preset and submit the modal.
-
- >,
- <>
- Last, copy the API key after your API key has been created
-
- >
- ],
-}]} />
\ No newline at end of file
+
+
+ Are you using Unraid pre-7.2? If yes, please first{" "}
+
+ follow the steps here to install the Unraid API
+
+ .
+ ,
+ <>
+ Open the settings and open the management access
+
+
+ >,
+ <>
+
+ Click on the "API Keys" tab at the top and click on the "create API key" button. Choose "Create new"
+
+
+
+ >,
+ <>
+
+ Enter a suitable name for your API key (e.g. Homarr), select the "Viewer" preset and submit the modal.
+
+
+
+ >,
+ <>
+ Last, copy the API key after your API key has been created
+
+
+ >,
+ ],
+ },
+ ]}
+/>
diff --git a/apps/docs/docs/integrations/uptime-kuma/index.mdx b/apps/docs/docs/integrations/uptime-kuma/index.mdx
index 63e6df82b..14834969f 100644
--- a/apps/docs/docs/integrations/uptime-kuma/index.mdx
+++ b/apps/docs/docs/integrations/uptime-kuma/index.mdx
@@ -1,43 +1,42 @@
---
-title: 'Uptime Kuma'
+title: "Uptime Kuma"
description: "Uptime Kuma is a self-hosted monitoring tool that tracks the availability of your services and websites."
hide_title: true
---
-import { IntegrationHeader } from '@site/src/components/integrations/header';
-import { IntegrationCapabilites } from '@site/src/components/integrations/widgets';
-import { AddingIntegration } from '@site/src/components/integrations/adding';
-import { IntegrationSecrets } from '@site/src/components/integrations/secrets';
-import { uptimeKumaIntegration } from '.';
-import { uptimeKumaWidget } from '@site/docs/widgets/uptime-kuma';
+import { IntegrationHeader } from "@site/src/components/integrations/header";
+import { IntegrationCapabilites } from "@site/src/components/integrations/widgets";
+import { AddingIntegration } from "@site/src/components/integrations/adding";
+import { IntegrationSecrets } from "@site/src/components/integrations/secrets";
+import { uptimeKumaIntegration } from ".";
+import { uptimeKumaWidget } from "@site/docs/widgets/uptime-kuma";
-
-
+
Uptime Kuma monitors the availability of your services and provides uptime statistics through its status page API.
### Widgets & Capabilities
-
+
+
### Adding the integration
+
### Secrets
-
+ ],
+ },
+ ]}
+/>
:::tip
If you don't specify a slug, Homarr will use `default` as the status page slug.
diff --git a/apps/docs/docs/management/api/index.mdx b/apps/docs/docs/management/api/index.mdx
index 2c5bd42e2..35e9f49e4 100644
--- a/apps/docs/docs/management/api/index.mdx
+++ b/apps/docs/docs/management/api/index.mdx
@@ -82,4 +82,33 @@ The OpenAPI and MCP APIs include board management endpoints for common automatio
- Set desktop and mobile home boards for the current user
- Configure global board defaults such as public home boards and status behavior
+Board layout responses identify each layout with a `mobile`, `base`, or `custom` role. Mobile and Base are protected. Layout-saving clients must preserve existing roles, keep Mobile at breakpoint `0`, keep Base as the highest breakpoint, and use unique custom breakpoints between them. The layout save operation returns the canonical saved layouts, including server-generated IDs for new custom layouts. A non-Base layout can be regenerated from Base with the protected reset operation.
+
Use the interactive API reference to inspect the exact request body for each endpoint under the `boards` and `settings` tags.
+
+## Partial integration failures
+
+Multi-integration widget queries keep data from integrations that remain available. Calendar and media-server results include
+an optional `error` on a failed integration; media-request responses expose a `failedIntegrations` array, and the latest
+requests endpoint returns its items in `requests`. When every selected integration fails, the query returns an error instead
+of a partial response.
+
+### Saving dashboard layouts
+
+The dashboard editor saves item positions and sections as one board update.
+Canvas height follows its content automatically and does not require a separate API setting.
+Responsive layouts store optional left and right gutter widths from zero to three columns.
+Gutter columns are reserved from the layout's total column count, and changing them reflows affected items.
+
+## Get Custom Widget authoring resources
+
+Custom JSX v2 provides four HTTP discovery endpoints:
+
+- `GET /api/custom-widgets/authoring-prompt` returns the current prompt plus MCP and HTTP resource identifiers.
+- `GET /api/custom-widgets/schema` returns the canonical widget JSON Schema.
+- `GET /api/custom-widgets/components` returns the release-matched static component catalog.
+- `GET /api/custom-widgets/skill` returns the concise live `SKILL.md` plus skills.sh, source, and install metadata. Retrieve the schema, component catalog, and examples through their dedicated resources only when needed.
+
+Call `customWidget_configurationRequestUser` to create a temporary API source setup URL where the user can confirm their server URL and enter credentials. Do not construct or save this URL.
+
+Authenticated tRPC and MCP procedures support definition management, validation, previews, template patches, and credential writes. An exported definition does not contain credentials.
diff --git a/apps/docs/docs/management/backup.mdx b/apps/docs/docs/management/backup.mdx
index 558afb82e..1c86837a9 100644
--- a/apps/docs/docs/management/backup.mdx
+++ b/apps/docs/docs/management/backup.mdx
@@ -24,12 +24,12 @@ Navigate to `Management` → `Tools` → `Backup`. You must have **admin** permi
Click the **Export** button in the export card. You'll be prompted to type `I understand` to confirm. Once confirmed, a ZIP file downloads containing:
-| File | Description |
-| -------------------- | ------------------------------------------------------------------- |
-| `db.sqlite` | Full SQLite database with all entities |
-| `metadata.json` | Backup metadata: Homarr version, creation timestamp, encryption key |
+| File | Description |
+| --------------- | ------------------------------------------------------------------- |
+| `db.sqlite` | Full SQLite database with all entities |
+| `metadata.json` | Backup metadata: Homarr version, creation timestamp, encryption key |
-The backup includes everything: apps, integrations, secrets, boards, users, groups, permissions, search engines, custom widgets, and server settings. (It just backs-up the whole database)
+The backup contains the complete database. It includes Custom Widgets and all other Homarr configuration.
## Restoring a Backup
diff --git a/apps/docs/docs/management/boards/index.mdx b/apps/docs/docs/management/boards/index.mdx
index 07323fc12..d0921fb5c 100644
--- a/apps/docs/docs/management/boards/index.mdx
+++ b/apps/docs/docs/management/boards/index.mdx
@@ -9,11 +9,11 @@ tags:
- Layout
---
-import iconColorImage from './img/icon-color.png'
-import itemRadiusImage from './img/item-radius.png'
-import layoutLargeImage from './img/layout-large.png'
-import layoutMediumImage from './img/layout-medium.png'
-import layoutSmallImage from './img/layout-small.png'
+import iconColorImage from "./img/icon-color.png";
+import itemRadiusImage from "./img/item-radius.png";
+import layoutLargeImage from "./img/layout-large.png";
+import layoutMediumImage from "./img/layout-medium.png";
+import layoutSmallImage from "./img/layout-small.png";
Each dashboard is called a "board".
You can create as many boards as you want.
@@ -24,24 +24,30 @@ Other boards can be viewed by direct links (e.g. my-homarr.com/board/smart-home)
Names of boards must be unique because this will be used in the URL to load the desired board.
## Create a new board
+
When creating a board, the availability of the board name will be checked for you.

### Name
+
The name must not include any special characters and must be at least one character long.
Underscores and hyphen-minus are allowed.
### Column count
-Homarr has a complex drag and drop system that can be used from the browser.
-The column count defines how many horizontal columns are available to drag and drop elements to.
+
+The column count defines how many fixed-width cells are available in each row.
+One cell contains a `200 × 200` logical-pixel tile.
+Homarr scales the whole board uniformly instead of changing widget proportions, with horizontal scrolling when further shrinking would make controls too small.
### Public
+
This will enable public access for everyone - regardless of whether they are logged in or not.
-This can be really useful if you want to have public boards in your company or for family and fiends.
+This can be really useful if you want to have public boards in your company or for family and friends.
Modifications for public boards must be allowed explicitly using user groups and their permissions.
## Board settings
+
You navigate to the board settings by clicking on these three dots:

@@ -50,8 +56,12 @@ Alternatively, you can also navigate from the board page itself using the cog ic

+All board settings are edited on a single page. Click the **Save changes** button in the save bar at the bottom to apply them, or **Discard** to restore the last saved values. The save bar only appears when you have unsaved changes. Access control and the Danger zone are independent sections and are applied immediately.
+
### General
+
#### Page title
+
This is the title that will be visible at the top left in the navigation bar:

@@ -59,6 +69,7 @@ This is the title that will be visible at the top left in the navigation bar:
This field is optional and Homarr will fall back to "Homarr" if nothing is set.
#### Meta title
+
This is the secondary title of the page that is not directly visible in the page itself.
Browser will use it to set the title of tabs:
@@ -67,78 +78,151 @@ Browser will use it to set the title of tabs:
Homarr will fall back to the board name and a suffix if nothing is set.
#### Logo image URL
+
This is the URL that is used for the image at the top left.
Homarr will fall back to the Homarr logo if nothing is set.
#### Favicon image URL
+
This is the URL that is used for the favicon in the tab title.
Homarr will fall back to the Homarr logo if nothing is set.
### Layout
-In this section you can configure the different responsive layouts. Depending on your screens width the layout will change.
+In this section you can configure the different responsive layouts. Homarr selects the layout with the highest breakpoint that is not wider than the current screen, then scales its complete fixed-size canvas uniformly.

-On each element the position and size of items can be changed seperately.
-When adding a new layout the position and sizes will automatically be generated depending on the column count.
-When adding a new item / dynamic section that will always be 1x1 on each layout and placed on the first available position.
+Every board has two protected layouts:
+
+- **Mobile** starts at `0px` and uses three columns on new boards.
+- **Base** is the widest layout and starts at `768px` on new boards.
+
+Mobile and Base can be renamed and their column counts and rails can be changed, but they cannot be removed. You can add removable custom layouts between their breakpoints.
+
+Each layout stores independent positions and sizes. Moving or resizing a widget while editing Mobile does not move it in Base or any custom layout. New custom layouts are generated from Base and scaled to their selected column count.
+
+Use **Reset from Base** on Mobile or a custom layout to replace all of its positions and sizes with a newly generated version of Base. The layout's name, breakpoint, and column count are kept.
+
+Every `1 × 1` tile has a `200 × 200` logical-pixel content area, with a separate `24` logical-pixel gap between tiles. Homarr scales the whole canvas to the available width without shrinking below 75%; narrower screens scroll horizontally so controls remain accessible.
+
+The board edge is a hard drag boundary. Moving a tile outside it cancels the placement, while adding content can still create the rows it needs. New items use their widget's default size and new Containers use a useful `4 × 3` size when space allows.
#### Name
-The name of the layout can be choosen freely. It is only used for the management and will not be visible on the board.
+The name of the layout can be chosen freely. It is only used for management and is not displayed on the board.
#### Column count
-Amount of columns for this layout. Defines how many items can be placed in a row. When changed it will automatically adjust the position and size of the items.
+The total number of columns in the layout. Changing it projects the saved positions and sizes into the new grid width without overlap.
+
+#### Fixed rails
+
+Each layout can reserve a fixed left rail, a fixed right rail, or both. Set a rail width to zero to disable it, or choose one to three columns. Reserved rail columns reduce the main canvas width. Apps, widgets, and Containers in rails use the same fixed cell sizes and remain visible while the page scrolls.
#### Breakpoint
-Minimum width of the screen where the layout will be used.
-Once the minmum of the next layout is reached the layout will change.
+The minimum screen width where the layout is used. Mobile is fixed at `0px`. Custom breakpoints must be unique and sit between Mobile and Base. Base must remain the highest breakpoint.
+
+#### View and edit modes
+
+View mode renders a static board without executing the drag-and-drop editor.
+After the board is interactive, Homarr prepares the editor in the background for users who can modify it.
+Entering edit mode keeps the static board visible until that preparation finishes, then enables collision-aware movement and resizing without changing the board geometry.
+Widget content is inert in edit mode, so dragging anywhere on the content moves the tile instead of activating the widget; its settings button remains visible in the compact header above the tile.
+Dropping a tile into another tile's exact slot swaps their positions when both resulting placements fit without overlapping other content.
+For other collisions, or when a tile grows into occupied space, nearby tiles are compacted out of the way.
+The visible corner controls provide a 44-pixel resize target, and edge controls appear on hover for horizontal or vertical resizing.
+Resize handles stop at the tile's minimum allowed size while the pointer is still moving.
+Exit edit mode to save the resulting positions.
+
+Pointer users can drag anywhere on a widget tile. Containers use the grab button at their top-left corner.
+Resize tiles from any edge or corner handle.
+On a touch screen, press and hold the tile before moving it; resize handles support direct touch input.
+Keyboard users can focus a tile, press Enter or Space to start editing, use the arrow keys to move it, and use Shift plus the arrow keys to resize it.
+Press Enter, Space, or Escape to stop keyboard editing.
+Keyboard moves and resizes apply immediately; Escape stops keyboard editing without undoing those changes.
+During a pointer or touch interaction, Escape cancels the move or resize and restores the layout from before that interaction.
+The **Move / resize item** action in the edit menu remains available for precise numeric input.
#### Preview
-
-
-
+Layout Settings shows a lightweight structural preview for every layout, including saved spans, Containers, widget labels, and app icons. The canvas width represents the screen range where that layout is active.
+
+Choose **Edit layout** to open the real board editor at that representative width on desktop. Save returns to Layout Settings. Cancel discards the local changes. On phones and tablets, the normal board editor continues to use the layout selected by the device width.
+
+
+
+
+
+### Containers and rails
+
+Containers can hold apps, widgets, and nested Containers.
+The same Container model works on the main canvas, inside a fixed rail, or inside another Container.
+Move and resize Containers directly in edit mode; nested content stays within the Container's local bounds.
+Resize outlines follow the pointer continuously while a dashed preview marks the grid size that will be applied on release.
+Each responsive layout stores its own Container position and size.
+
+Each Container can configure:
+
+- Its label and whether that label is visible.
+- Whether users can collapse and expand it like an accordion.
+- Whether its menu shows **Open all in new tabs** for the app links it contains.
+- Its border color and custom CSS classes.
+
+Collapsing a Container reduces its full-width header to a half-row expansion bar and compacts the surrounding layout without losing its expanded size.
+Expanding it smoothly restores those bounds and moves following items out of the way without reloading the widgets inside it.
### Background
+
#### Background image URL
+
Homarr can display a background image behind apps and widgets.
This URL can be used to define what image should be shown.
#### Background image attachment
+
This defines whether the background should move when the user scrolls.
#### Background image size
+
This defines whether the background should cover the entire page (and possibly be cropped) or never be cropped but may not cover the entire page.
#### Background image repeat
+
This defines whether the background should be repeated if it cannot cover the entire page.
This option can be useful if you have patterns that can be repeated easily (e.g. patterns):

### Appearance
+
#### Primary color
+
Color that can be chosen freely to give accents to active elements.
+
#### Secondary color
+
Color that can be chosen freely to give accents.
+
#### Opacity
+
Opacity of the colors.
+
#### Icon color
+
Change the color of your bookmark / app icons.
-
+
#### Item radius
-Change the radius of your items / categories.
-
+Change the radius of your items and Containers.
+
### Custom CSS
+
This is the field where you can enter custom CSS.
The styling will only be applied on the respective board page.
@@ -151,14 +235,30 @@ editable review form before requesting approval to save the change.
### Behaviour
#### Disable app status
+
This will disable the status check for apps.
+#### Work directly from the board
+
+Widgets expose their routine controls without requiring edit mode:
+
+- Hold `Shift` while hovering a widget that supports advanced view to expand it in place while the other widgets
+ remain visible but dimmed. Release `Shift` to close it. Focus that widget and press `Shift` + `Enter` to keep the view
+ open until you close it. On touch devices, open it from the widget menu. Other widgets adapt in place instead.
+- Right-click a widget to inspect its live query status, refresh its data, change quick options, open settings, or run
+ supported actions. Available controls follow your board and integration permissions.
+- If you can modify the board, right-click or touch and hold an empty grid cell to add an item there. Focus a board
+ section and press `Shift` + `F10` for the keyboard equivalent.
+
+Widget and empty-space context menus follow the **Enable right click on widgets** preference in your user settings.
+
### Access control
+
With the access control, you can permit a single user or a group to your board.
The users and groups can have one of these three levels of access:
| **Permission Level** | View board | Edit mode | Settings | Danger zone (eg. delete) |
-|----------------------|------------|-----------|----------|--------------------------|
+| -------------------- | ---------- | --------- | -------- | ------------------------ |
| View board | X | | | |
| Modify board | X | X | X | |
| Full access | X | X | X | X |
@@ -177,22 +277,27 @@ After editing permissions, the save button below the table must be pressed.
Otherwise, all changes will be discarded.
### Danger zone
+
#### Rename
+
Renaming a board will break all existing links to it permanently with no redirect.
#### Board visibility
+
By default, boards will be private. This means only authenticated users that are authorized have access.
Making a board public will expose it to everyone - including unauthenticated users.
#### Delete
+
This will permanently delete the board.
This action is irreversible and cannot be undone.
## Delete boards
+
The deletion of boards is permanent and cannot be undone.
There are two ways to delete a board:
-- Navigate to the settings of your board, open the "Danger zone" dropdown and click on the "delete this board" button.
+- Navigate to the settings of your board, open the "Danger zone" section and click on the "delete this board" button.
- Navigate to the list of boards in the management pages, click the three dots and click on "delete permanently".
Both actions will execute the same deletion process.
@@ -200,7 +305,7 @@ Both actions will execute the same deletion process.
## Duplicate boards
:::note
-Duplicating a board will not copy the users and groups that have access to the board
+Duplicating a board will not copy the users and groups that have access to the board
nor the integrations the user does not have access to.
:::
@@ -210,47 +315,51 @@ You can duplicate a board by clicking on the three dots in the list of boards an

## Setting boards as your home board
+
The home board is the dashboard, that will be shown at the root path of Homarr or if no board name was passed in the URL.
All the following paths will lead to the home board:
-- ``my-homarr.com``
-- ``my-homarr.com/boards``
+
+- `my-homarr.com`
+- `my-homarr.com/boards`
Your home board will have a special small badge at the top right in the list of boards:

### For yourself
+
To set the home board yourself, the easiest option is to set it directly in the list of boards:

Alternatively, you can also define it via your user profile:
+
1. Click on your user profile at the top right
-2. Click on ``Your preferences``
-3. Change the home board in the ``home board`` dropdown and click on ``Save``.
+2. Click on `Your preferences`
+3. Change the home board in the `home board` dropdown and click on `Save`.
### For other users
-1. Navigate to the users: ``Management`` > ``Users`` > ``Manage``
+1. Navigate to the users: `Management` > `Users` > `Manage`
2. Click on the username of the user that you want to adjust
-3. Select the desired home board via the dropdown and click on ``Save`` \
-
+3. Select the desired home board via the dropdown and click on `Save` \
+ 
### For user groups
-1. Navigate to the user groups: ``Management`` > ``Users`` > ``Groups`` > ``Manage``
-2. Go to tab ``Settings``
-3. Select the desired home board via the dropdown and click on ``Save`` \
-
+1. Navigate to the user groups: `Management` > `Users` > `Groups` > `Manage`
+2. Go to tab `Settings`
+3. Select the desired home board via the dropdown and click on `Save` \
+ 
### For whole server
You can also set a global home board that will be used for all users that have not set a home board themselves.
It will also be used for anonymous users.
-1. Navigate to the settings: ``Management`` > ``Settings`` > ``Boards``
-2. Select the desired home board via the dropdown and click on ``Save`` \
-
+1. Navigate to the settings: `Management` > `Settings` > `Boards`
+2. Select the desired home board via the dropdown and click on `Save` \
+ 
:::note
For the global home board you can only select boards that are public.
diff --git a/apps/docs/docs/management/certificates/index.mdx b/apps/docs/docs/management/certificates/index.mdx
index cceaa54fd..aa843bfe0 100644
--- a/apps/docs/docs/management/certificates/index.mdx
+++ b/apps/docs/docs/management/certificates/index.mdx
@@ -7,27 +7,29 @@ tags:
---
# Certificates
-On this page you can manage your trusted certificates.
+
+On this page you can manage your trusted certificates.
For example if you use a self signed certificate for one of the integrations you want to connect, you can add it here.
-Below you can see a screenshot of the certificates page.
-The items show the subject, filename and when it expires.
+Below you can see a screenshot of the certificates page.
+The items show the subject, filename and when it expires.
The color of the icon indicates how long it is valid.

-| Color | Meaning |
-| --- | --- |
-| 🟩 Green | Valid for more than 30 days |
+| Color | Meaning |
+| --------- | --------------------------- |
+| 🟩 Green | Valid for more than 30 days |
| 🟨 Yellow | Valid for less than 30 days |
-| 🟧 Orange | Valid for less than a week |
-| 🟥 Red | Valid for less than a day |
-
+| 🟧 Orange | Valid for less than a week |
+| 🟥 Red | Valid for less than a day |
## Opening the certificates page
-Navigate to ``Management`` > ``Tools`` > ``Certificates``.
+
+Navigate to `Management` > `Tools` > `Certificates`.
## Add a certificate
+
To add a certificate click on the "Add certificate" button.

@@ -62,4 +64,4 @@ Below you can find instructions for some of the integrations.
Since version 6 of Pi-hole, the web interface can be accessed through HTTPS.
The certificate can be found in the path `/etc/pihole/tls_ca.crt`. To add the certificate, this file needs to be copied and uploaded to Homarr.
-You can find more details regarding SSL / TLS for Pi-hole [here](https://docs.pi-hole.net/api/tls/).
\ No newline at end of file
+You can find more details regarding SSL / TLS for Pi-hole [here](https://docs.pi-hole.net/api/tls/).
diff --git a/apps/docs/docs/management/custom-widgets/_category_.json b/apps/docs/docs/management/custom-widgets/_category_.json
new file mode 100644
index 000000000..23e215294
--- /dev/null
+++ b/apps/docs/docs/management/custom-widgets/_category_.json
@@ -0,0 +1,9 @@
+{
+ "label": "Custom Widgets",
+ "collapsed": true,
+ "position": 8,
+ "link": {
+ "type": "doc",
+ "id": "management/custom-widgets/index"
+ }
+}
diff --git a/apps/docs/docs/management/custom-widgets/agent-authoring.mdx b/apps/docs/docs/management/custom-widgets/agent-authoring.mdx
new file mode 100644
index 000000000..25afa3724
--- /dev/null
+++ b/apps/docs/docs/management/custom-widgets/agent-authoring.mdx
@@ -0,0 +1,104 @@
+# Use agents and skills.sh
+
+An external agent creates one widget at a time with the Homarr MCP server. The agent harness manages the list for multiple widgets.
+
+Homarr does not create widget packs or drafts for multiple widgets.
+
+The MCP API key must belong to a Homarr administrator. Non-administrator keys do not list Custom Widget tools, prompts,
+or resources. The same administrator requirement applies to the matching HTTP authoring endpoints.
+
+
+ View the Custom Widget Skill on skills.sh
+
+
+Install the Agent Skill from the verified Homarr repository:
+
+```sh
+npx skills add https://github.com/homarr-labs/homarr --skill homarr-custom-widget
+```
+
+The portable skill gives concise authoring guidance. The Homarr MCP prompt gives connected agents a retrieval workflow: read the live schema, then fetch only the component documentation needed for the widget.
+
+The workbench's **Copy AI prompt** action is separate. It creates a self-contained prompt below 12,000 characters for an ordinary ChatGPT or Claude conversation. It includes the lean contract, bindings, recommended components, and two polished examples. It never tells an offline model to call MCP tools.
+
+The source is in [`.agents/skills/homarr-custom-widget`](https://github.com/homarr-labs/homarr/tree/HEAD/.agents/skills/homarr-custom-widget). `HEAD` follows the repository default branch, so the link is not tied to an unreleased feature branch. Homarr does not provide a ZIP archive.
+
+After publication, run the installation command once. This action registers installation activity.
+
+Then, verify the skills.sh page. Request official maker attribution for `homarr-labs` from skills.sh.
+
+Repository code cannot give this attribution.
+
+## Get MCP resources
+
+- Prompt: `homarr-custom-widget-author`
+- Resources: `homarr://custom-widgets/schema`, `components`, `components/{name}`, `examples/{name}`, and `skill`
+- HTTP: `GET /api/custom-widgets/authoring-prompt`, `schema`, `components`, and `skill`
+
+The authoring-prompt tool description contains one sentence. The prompt contains the workflow instructions and resource links.
+
+`GET /api/custom-widgets/skill` and the MCP tool return the concise skill discovery payload:
+
+```ts
+{
+ name: "homarr-custom-widget";
+ version: string;
+ skillMd: string;
+ skillsShUrl: string;
+ sourceUrl: string;
+ installCommand: string;
+}
+```
+
+The full installed component catalog stays available through `homarr://custom-widgets/components` and per-component resources. It is deliberately not embedded in the skill or clipboard prompt.
+
+These interfaces do not return an archive URL or archive hash.
+
+## Create a Custom Widget with MCP
+
+1. Get the prompt, live schema, and component catalog.
+2. Read the external API documentation.
+3. Build one definition.
+4. Validate the definition without saving it.
+5. Create a preview session.
+6. Test the queries and actions.
+7. Open the preview URL with browser tools.
+8. Inspect the redacted journal and revise the local candidate until it is valid.
+9. Create the widget, including any source URLs and credentials the user already supplied.
+10. Otherwise, ask the user to configure the saved definition with the ten-minute Homarr source-setup URL.
+11. Create a final preview with the saved definition ID so Homarr loads its stored credentials, then test it again.
+12. Update the saved definition if the final verification finds a problem.
+13. Repeat the procedure for the next widget.
+
+Each source-configuration URL applies to one definition or preview and one source. It is short-lived, single-use, and
+can only be opened and completed by an authenticated Homarr administrator. The administrator confirms the server URL,
+network scope, and required credential fields directly in Homarr. The agent receives only request status and never
+receives plaintext credentials. Preview-scoped values expire with that preview; definition-scoped values persist.
+
+Agents can install existing widgets without regenerating them:
+
+1. Search with `customWidget_workshopSearch`.
+2. Inspect and validate a result with `customWidget_workshopGet`.
+3. Install it with `customWidget_workshopInstall`.
+4. Configure deployment-specific URLs and supplied credentials with `customWidget_sourceConfigure`.
+5. If the agent does not have those values, call `customWidget_configurationRequestUser` and wait for completion.
+6. Test the installed definition, then use `board_addItem` with kind `customApi` and the returned definition ID.
+
+Homarr encrypts the plaintext immediately. Homarr does not return the plaintext.
+
+## Evaluate the copied prompt
+
+Homarr maintainers can run the same self-contained prompt that the workbench copies through the live generation-quality suite:
+
+```sh
+OPENROUTER_API_KEY=... pnpm --filter @homarr/custom-widgets test:ai
+```
+
+The suite uses DeepSeek V4 Pro to author Pokédex, Portainer, football, Jellyfin, and Home Assistant widgets. Before DeepSeek V4 Flash judges a result, it must pass Homarr's real clipboard parser, canonical schema, and deterministic scenario checks for the documented API routes, authentication, bindings, actions, and essential runtime components. It saves every response, validated widget manifest, and detailed category scores under `packages/custom-widgets/.ai-evaluations/`.
+
+Each scenario stops as soon as it passes and has a hard limit of ten generation attempts. Override the models or
+per-scenario limit with `OPENROUTER_GENERATOR_MODEL`, `OPENROUTER_JUDGE_MODEL`, and
+`CUSTOM_WIDGET_AI_MAX_LOOPS`. Generated artifacts are local and ignored by Git; promote a reviewed, validated result
+into the bundled-widget source deliberately.
+
+The reference API check uses a real PokeAPI list/detail request and a read-only Portainer container query when `PORTAINER_BASE_URL` and `PORTAINER_API_KEY` are set. Use `pnpm --filter @homarr/custom-widgets test:reference-apis -- --pokeapi-only` for the public PokeAPI check alone. To exercise start, restart, and stop against a disposable container, also set `PORTAINER_TEST_CONTAINER_ID` and the explicit safeguard `PORTAINER_ALLOW_MUTATIONS=1`; the script restores the container's original running state.
diff --git a/apps/docs/docs/management/custom-widgets/authoring.mdx b/apps/docs/docs/management/custom-widgets/authoring.mdx
new file mode 100644
index 000000000..429827c55
--- /dev/null
+++ b/apps/docs/docs/management/custom-widgets/authoring.mdx
@@ -0,0 +1,38 @@
+# Create a Custom Widget
+
+import { CanonicalCustomWidgetExample } from "@site/src/components/custom-widget-code";
+
+The workbench is visual-first: configure the default API connection, add request cards, add option rows, then write JSX. Additional sources and raw manifest JSON remain under Advanced. Code and preview sit side by side on desktop and use tabs on mobile.
+
+## Manual workflow
+
+1. Configure the default API source, network scope, authentication, and credential.
+2. Add query/action cards. Values can be literals, widget options, or invocation parameters.
+3. Add widget options with their control and default value.
+4. Write responsive JSX using `data`, `status`, `options`, and temporary `inputs`.
+5. Test and preview, inspect Data and the redacted request journal, then save.
+
+
+
+
+
+Every option has a default. Supported controls include text, textarea, number, switch, select, multi-select, slider, date, time, color, icon, URL, duration, time zone, and JSON. Dynamic choices use `choicesFrom` with a query ID and value/label paths.
+
+## ChatGPT or Claude
+
+Enter the desired widget and optional API documentation URL, then select **Copy AI prompt**. The single self-contained prompt is below 12,000 characters, starts with your request, includes the lean contract and two polished examples, and never claims that an offline chat has MCP tools.
+
+The model must return one fenced `json` block containing the complete widget, including the JSX in its `template`
+field. Copy that single block and select **Paste AI response**. Homarr fills the visual builders and the JSX editor
+without any placeholder replacement or second paste.
+
+Homarr shows all actionable schema or JSX errors inline. Enter credentials separately. The prompt deliberately
+recommends a focused Mantine component set, visual hierarchy, responsive layout, theme compatibility, and loading,
+empty, failure, and success states.
+
+Connected agents should use the skills.sh skill and retrieve the live schema and individual component metadata only as needed.
diff --git a/apps/docs/docs/management/custom-widgets/component-reference.mdx b/apps/docs/docs/management/custom-widgets/component-reference.mdx
new file mode 100644
index 000000000..15adc109a
--- /dev/null
+++ b/apps/docs/docs/management/custom-widgets/component-reference.mdx
@@ -0,0 +1,13 @@
+---
+title: Component reference
+sidebar_position: 3
+tags: [Custom Widgets, JSX, Mantine, Components]
+---
+
+import { CustomJsxComponentReference } from "@site/src/components/custom-jsx-component-reference";
+
+# Custom JSX component reference
+
+Search the components and properties exposed by Homarr's safe Custom JSX runtime. The live schema and diagnostics remain authoritative: properties marked as blocked cannot be passed from widget code, and denied components are shown only for explanation.
+
+
diff --git a/apps/docs/docs/management/custom-widgets/custom-jsx.mdx b/apps/docs/docs/management/custom-widgets/custom-jsx.mdx
new file mode 100644
index 000000000..a8bb3bd85
--- /dev/null
+++ b/apps/docs/docs/management/custom-widgets/custom-jsx.mdx
@@ -0,0 +1,45 @@
+# Use the safe JSX runtime
+
+Templates are JSX expressions interpreted inside a widget-local safety and render budget. They can read:
+
+- `data.requestId` — the latest response
+- `status.requestId` — `{ loading, ok, status, statusText, error }`
+- `options.name` — saved widget configuration
+- `inputs.name` — temporary values from declarative `bind`
+
+Use normal Mantine layouts, typography, feedback, dates, and charts. Official compound names such as `Card.Section`, `Tabs.List`, and `Radio.Group` are supported. Ordinary safe props pass through; a dangerous capability produces an explicit error.
+
+## Temporary inputs
+
+```jsx
+
+
+
+ {(results) => (
+
+ {(results ?? []).map((item) => (
+ {item.name}
+ ))}
+
+ )}
+
+
+```
+
+Bound values live only in React memory while the widget is mounted. They are not saved to the database, board options, exports, browser storage, or `localStorage`. Remounting resets them. Components can share a binding when their adapter types agree.
+
+## Expression language
+
+Use expressions, conditionals, optional chaining, arrays, objects, template strings, safe standard helpers, and expression callbacks for `map`, `filter`, `sort`, and trusted runtime slots. Callback blocks, authored `const`, IIFEs, authored recursion, arbitrary functions, imports, hooks, refs, raw events, browser requests, `eval`, and bigint are blocked.
+
+Bounded regex literals are supported for string matching, splitting, search, and replacement. Patterns with lookbehind, backreferences, nested quantifiers, excessive length, or unsupported flags are rejected.
+
+## Runtime components
+
+- `SubFetch` runs a manual query with its `params`. Set `triggerContent` to a card or image to make that content the accessible click and keyboard trigger instead of showing the default load button.
+- `ActionButton` and `ToggleSwitch` run actions.
+- `RefreshButton` renders a compact, accessible icon control that refreshes load queries for authenticated viewers. It
+ is disabled for anonymous public-board viewers because refreshing invalidates shared server cache state.
+- `Icon`/`TablerIcon` resolves installed safe icons.
+
+Always include responsive loading, empty, error, and success states. Runtime failures remain inside the widget tile.
diff --git a/apps/docs/docs/management/custom-widgets/index.mdx b/apps/docs/docs/management/custom-widgets/index.mdx
index 3bc624bb7..5d054fe89 100644
--- a/apps/docs/docs/management/custom-widgets/index.mdx
+++ b/apps/docs/docs/management/custom-widgets/index.mdx
@@ -1,216 +1,134 @@
---
-tags:
- - Custom Widgets
- - API
- - Widgets
- - JSX
+title: Custom Widgets
+sidebar_label: Overview
+tags: [Custom Widgets, API, JSX, MCP]
---
+import { BundledCustomWidgetGallery, CustomWidgetCodeExample } from "@site/src/components/custom-widget-code";
+
+export const completeManifest = `{
+ "$schema": "homarr-custom-widget-v2",
+ "name": "Service status",
+ "sources": {
+ "default": { "name": "Service API", "baseUrl": "https://status.example.com", "networkScope": "public", "auth": "none" }
+ },
+ "requests": { "status": { "path": "/api/status" } },
+ "options": {},
+ "template": "{data.status?.label ?? 'Unknown'}"
+}`;
+
# Custom Widgets
-:::warning
+:::warning Experimental feature
-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).
+This feature is experimental and will probably change. If you encounter bugs, need more capabilities, or find something confusing, tell us in the [official discussion thread](https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets). Your feedback directly shapes what comes next. Happy hacking!
:::
-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.
+Custom JSX v2 is Homarr's only Custom Widget format. A definition contains keyed API sources, keyed requests, optional widget options, and one safe JSX template. Credentials are encrypted separately and never exported.
-## Creating a Custom Widget
-
-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** - 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)
-
-## Display Types
-
-| 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 |
-
-### Design with AI
-
-The **Design with AI** action is available while creating or editing a custom widget. Describe what the
-widget should show and, when [Homarr Assistant](../assistant) is enabled, send the request directly from
-the form. The assistant receives the widget schema, the current non-secret configuration, and the latest
-test response. It can then propose and run the matching create or update tool after you approve the
-mutation in the conversation.
-
-Run **Test** first when possible so the prompt includes the endpoint's real response shape. Saved secret
-values are never added to the prompt. **Copy to clipboard** remains available when Homarr Assistant is
-disabled or when you prefer to use another AI service such as ChatGPT, Claude, or Gemini.
-
-
-Example: Pokédex widget using Custom JSX
-
-```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": "\n \n \n Pokédex\n \n \n \n \n {data.results.map((pokemon, i) =>\n \n \n \n \n \n #{i + 1}\n {pokemon.name}\n \n \n \n \n \n \n )}\n \n \n"
- }
+```ts
+interface HomarrCustomWidgetV2 {
+ $schema: "homarr-custom-widget-v2";
+ name: string;
+ description?: string;
+ iconUrl?: string;
+ sources: Record;
+ requests: Record;
+ options?: Record;
+ template: string;
}
```
-
+### Design with AI
-## Importing and Exporting
+The **Design with AI** action is available while creating or editing a custom widget. Describe what the widget should show and, when [Homarr Assistant](../assistant) is enabled, send the request directly from the form. The assistant receives the widget schema, current non-secret configuration, and latest test response, then proposes the matching create or update tool for your approval.
-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.
+Run **Test** first when possible so the prompt includes the endpoint's real response shape. Saved secrets are never added to the prompt. **Copy to clipboard** remains available when the Assistant is disabled or when you prefer another AI service.
-The import format uses `"$schema": "homarr-custom-widget-v2"` to identify the widget version.
+
-## Available Mantine Components for Custom JSX
+The browser renders JSX. Homarr's server executes named requests, applies board permissions and network protections, and injects credentials. The browser never receives source credentials.
-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.
+## Permissions
-### Layout
+Custom Widget management is an administrator-only surface. Only a Homarr administrator can create, import, migrate,
+preview, publish, install, configure, or write secrets, including through HTTP authoring resources or MCP. An API key
+only lists Custom Widget MCP tools, prompts, and resources when its owner is an administrator.
-| 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) |
+Adding or configuring a Custom Widget placement requires both administrator access and permission to modify that
+board. A non-administrator with board-modify access can move, resize, change presentation settings, or remove an
+existing placement, but cannot add one, change its definition, refresh interval, or Custom Widget options. Duplicating
+a board that contains Custom Widgets also requires an administrator.
-### Typography & Content
+Once an administrator has placed a widget, ordinary board permissions apply to rendering and requests. Authorized
+non-administrators can use it on private boards, and queries can render for anonymous viewers on public boards.
+Mutations remain authenticated and enforce each request's view, modify, or full permission.
-| 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) |
+## Authoring paths
-### Data Display
+- [Create a Custom Widget](./authoring) visually or with one copied AI prompt.
+- [Use the JSX runtime](./custom-jsx) for data, Mantine components, temporary inputs, and diagnostics.
+- [Browse the supported component reference](./component-reference).
+- [Configure requests and security](./requests-and-security).
+- [Use MCP and the skills.sh skill](./agent-authoring).
+- [Correct problems](./troubleshooting).
-| 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) |
+The workbench keeps one continuous layout with visual source, request, and option editors. Advanced raw JSON remains available for experts and AI paste. Invalid values stay inline and disable both preview and Save/Create until they are corrected.
-### Tables
+Exports, imports, the official Workshop client, and MCP validate the same schema. PocketBase stores community
+submissions without interpreting their content, and Homarr validates them before installation. Widgets saved in the v1
+format remain visible to administrators with a **Migration required** badge after upgrading. Use **Copy migration
+prompt**, give the redacted prompt to an LLM, copy its single complete JSON block, then select **Paste migrated widget**
+from that widget's action menu. Homarr reads the `template` value into the JSX editor automatically. The prompt keeps safe
+request metadata but deliberately omits the v1 display configuration because legacy free-text and JSX can contain
+hard-coded credentials. Add a credential-free description of the widget's visible layout and behavior when sending the
+prompt.
-| 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) |
+Homarr keeps the original v1 definition and encrypted secret as a compatibility backup even after the validated v2
+replacement is created. The v2 record uses the same stable ID and takes precedence. The archived v1 copy stays hidden
+while the replacement exists; deleting the v2 record surfaces the v1 entry as migration-required again. Automatic
+legacy deletion is intentionally unavailable during v2 stabilization. Credentials are never included in prompts,
+logs, or exports.
-### Lists
+For administrators adding widgets while editing a board, the Custom Widgets section includes **Install from
+Workshop**. Non-administrators do not see the Custom Widget picker or Workshop actions. The Workshop and API source
+review open above the widget picker without closing it. After import, the new definition appears in the picker. Its tile
+waits for the board to be saved before server requests run and loads automatically after a successful save; no page
+reload is required.
-| Component | Doc |
-| ---------------------------- | ----------------------------------------- |
-| `List` / `List.Item` | [docs](https://mantine.dev/core/list) |
-| `Timeline` / `Timeline.Item` | [docs](https://mantine.dev/core/timeline) |
+## Import and Workshop
-### Feedback & Loading
+Import review shows origins, authentication modes, network scopes, methods, actions, and permissions. Every source has a suggested URL, but private and loopback sources must be confirmed or replaced for the current Homarr installation. The same screen accepts API keys, tokens, usernames, and passwords; credentials remain optional at install time and missing values stay visible on the Custom Widgets page.
-| 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) |
+For example, installing **Tautulli Activity** asks for both the user's Tautulli URL and API key. A stable public source such as PokéAPI keeps its published URL without creating unnecessary setup work.
-### Navigation & Overlay
+Workshop publishes technically valid widgets immediately; users can vote or report them, and Workshop moderators can
+delete submissions. Community moderation is separate from Homarr administration and is not a security boundary.
+Publishers should replace personal private-network hosts with a clear service placeholder before publishing. Homarr
+performs definitive validation again at install time.
-| 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) |
+Connected agents can search, inspect, and install Workshop widgets with `customWidget_workshopSearch`, `customWidget_workshopGet`, and `customWidget_workshopInstall`. After installation they can set a source URL and credentials with `customWidget_sourceConfigure`, or create a secure user-facing setup URL with `customWidget_configurationRequestUser`. Secret values are never returned through MCP.
-### Charts
+These tools are only exposed to API keys owned by Homarr administrators. Opening and completing a source-configuration
+URL also requires the same authenticated administrator; the URL is short-lived, single-use, and redacted.
-| 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) |
+## Reliability and rollback
-### Interactive Components
+Custom Widget response caches and action invalidation use shared Redis state, so multiple Homarr replicas see the same
+versioned cache generation. Deployments must point every replica at the same Redis service.
-| 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) |
+Before upgrading, back up the database and `SECRET_ENCRYPTION_KEY`; a v1 binary can still read preserved v1 tables
+during application rollback. Widgets created only in v2 may be unavailable to the rolled-back binary, but do not
+replace the v1 backup.
-## Template Bindings
+## Bundled examples
-Your template receives:
+Homarr seeds five disabled examples without credentials and never overwrites an edited seed. They use the same public contract. The Tautulli example is the highest-scoring widget preserved from Homarr's AI authoring evaluation:
-- `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
-
-## Security Constraints
-
-- 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
+
diff --git a/apps/docs/docs/management/custom-widgets/requests-and-security.mdx b/apps/docs/docs/management/custom-widgets/requests-and-security.mdx
new file mode 100644
index 000000000..1cae3ced1
--- /dev/null
+++ b/apps/docs/docs/management/custom-widgets/requests-and-security.mdx
@@ -0,0 +1,53 @@
+# Configure requests and security
+
+Most widgets use a single source named `default`. Extra keyed sources are available under Advanced for widgets that combine APIs. Authentication is `none`, `bearer`, `basic`, an API-key header, or an API-key query parameter. Credentials belong to the widget but remain outside its manifest.
+
+## Named requests
+
+Requests are keyed by ID. `source`, `kind`, `method`, `trigger`, `auth`, and `permission` have sensible defaults, so a load query can be as small as:
+
+```json
+{ "status": { "path": "/api/status" } }
+```
+
+The request `path` is appended to the source `baseUrl`, including a base path such as `/api/v2`.
+
+GET, POST, PUT, and PATCH work for queries and actions. DELETE is restricted to actions so it can never run while a
+board loads. Kind controls the remaining behavior: load queries run automatically, manual queries run through
+`SubFetch`, and actions run only from `ActionButton` or `ToggleSwitch`.
+
+Bind values directly where they are used:
+
+```json
+{
+ "containers": {
+ "path": "/api/endpoints/{option:endpointId}/docker/containers/json",
+ "query": { "all": { "$option": "showAll" } }
+ },
+ "restart": {
+ "kind": "action",
+ "method": "POST",
+ "path": "/api/endpoints/{option:endpointId}/docker/containers/{param:id}/restart",
+ "invalidates": ["containers"]
+ }
+}
+```
+
+`{option:name}` and `$option` read saved widget options. `{param:name}` and `$param` read invocation-time `params`. Load queries cannot use invocation parameters. Homarr infers the exact parameter names and primitive values from these references, rejecting missing or extra invocation params at the request boundary.
+
+Paths and query-string values must resolve to text, numbers, or booleans. JSON bodies can also use structured `json` and `multiSelect` option values, for example `{ "entity_ids": { "$option": "entities" } }`.
+
+## Protection
+
+Every request uses Homarr's existing server executor: same-origin source enforcement, network scope, DNS and redirect validation, SSRF protection, board permissions, rate and request limits, body and response limits, timeouts, and encrypted credential injection. Static headers cannot override credentials, cookies, routing, proxy, forwarding, or hop-by-hop headers.
+
+Queries default to view permission and actions to modify. DELETE always requires full permission and confirmation. Actions are never cached, retried, or run on load.
+
+Management and runtime are intentionally separate. A Homarr administrator with board-modify permission places and
+configures the widget. After placement, query and action authorization follows the request's board permission. Public
+boards may execute queries for anonymous viewers, but mutations stay authenticated. This includes shared-cache
+invalidation: `RefreshButton` is disabled for anonymous viewers, while ordinary scheduled query rendering remains
+available.
+
+Responses use bounded, versioned shared-Redis cache keys. Action invalidation advances the shared version so every
+Homarr replica stops serving the previous response without scanning or deleting a key prefix.
diff --git a/apps/docs/docs/management/custom-widgets/troubleshooting.mdx b/apps/docs/docs/management/custom-widgets/troubleshooting.mdx
new file mode 100644
index 000000000..068d8a21e
--- /dev/null
+++ b/apps/docs/docs/management/custom-widgets/troubleshooting.mdx
@@ -0,0 +1,83 @@
+# Correct Custom Widget problems
+
+## Correct an unavailable widget
+
+The definition does not exist. If it was deleted, remove the unavailable tile from the board.
+
+If the Custom Widgets page shows the definition with a **Migration required** badge, an administrator can open its
+action menu and select **Copy migration prompt**. Give that redacted prompt to an LLM, then import the resulting v2 JSON
+from the same menu. The LLM returns one complete JSON block; copy it once and select **Paste migrated widget**. Homarr
+places its `template` value into the JSX editor automatically. Because arbitrary v1 display text or JSX may contain
+credentials, the prompt omits that display configuration; describe the old widget's visible layout and behavior without
+including secrets. Other viewers see **Contact an administrator** instead of a migration link.
+
+Homarr keeps the v1 definition and encrypted secret after migration. The validated v2 replacement uses the same ID and
+takes precedence; the archived v1 record reappears if the v2 replacement is deleted. There is no v1 permanent-delete
+action during the v2 stabilization period.
+
+Otherwise, install or create a Custom JSX v2 definition.
+
+## Correct an invalid saved definition
+
+The Custom Widgets page isolates a definition that does not conform to the current schema. The other definitions remain available.
+
+An invalid definition shows its validation errors. You can delete it.
+
+You cannot edit, enable, export, duplicate, publish, or select an invalid v2 definition. Recreate the definition or import a valid definition. Preserved v1 definitions use the separate **Migration required** workflow described above.
+
+## Correct validation errors
+
+Read the first schema or JSX diagnostic. Use its path to find the invalid value.
+
+Make sure keyed API source and request names are valid and unique.
+
+Make sure that each named request references an API source. Use explicit `$option` and `$param` references.
+
+Make sure every option default matches its control. Use the correct request kind for each runtime component.
+
+An incomplete API source URL or icon URL causes a form validation error. The workbench keeps the draft open.
+
+The workbench marks the invalid field while you type. An invalid URL does not replace the form with the error boundary.
+
+An invalid definition disables widget preview and Save/Create. The Data, Options, Actions, and Diagnostics tabs remain available so you can correct it.
+
+The Diagnostics tab shows the current schema errors.
+
+## Workshop unavailable
+
+If Workshop is unavailable, check `WORKSHOP_API_URL` and PocketBase health. Widgets that are already installed continue
+to work.
+
+## Correct request errors
+
+Check the API source network scope. Check the base URL and credentials.
+
+Check the request method, path, body, direct value bindings, and board permission.
+
+For “invalid placeholder,” use `{option:name}` or `{param:name}`. Load queries can only reference options. The preview journal does not show credentials.
+
+The journal shows the method, request ID, path template, status, and duration.
+
+In a multi-instance deployment, all Homarr replicas must share Redis. Versioned cache invalidation is distributed
+through Redis; replicas connected to different Redis databases can serve stale query responses after an action.
+
+## Correct a Mantine prop warning
+
+`UNKNOWN_MANTINE_PROP` is a warning. Homarr passes the prop to Mantine.
+
+Check the prop in the documentation for the installed Mantine version. `BLOCKED_CAPABILITY` identifies an unsafe capability.
+
+Use safe widget options, temporary inputs, or named requests instead.
+
+## Repair an existing item
+
+The widget option definitions changed and the saved values are invalid. Open the item settings.
+
+Correct the marked configuration values. Homarr does not infer widget option migrations.
+
+## Roll back safely
+
+Before an application upgrade, back up the database and preserve the exact `SECRET_ENCRYPTION_KEY`. The v2 schema keeps
+the physical v1 definition and secret tables readable by a v1 binary. Roll back the application without running
+manual cleanup, verify legacy widgets, and restore the database backup if the migration itself must be reversed.
+PocketBase Workshop data is a separate backup and restore operation.
diff --git a/apps/docs/docs/management/integrations/index.mdx b/apps/docs/docs/management/integrations/index.mdx
index 51b3424d3..de819e692 100644
--- a/apps/docs/docs/management/integrations/index.mdx
+++ b/apps/docs/docs/management/integrations/index.mdx
@@ -6,11 +6,13 @@ tags:
---
# Integrations
+
Integrations are connections to supported third party applications.
This page will describe the process of managing integrations but the process may vary depending on your specific integration.
Check [the available integrations](/docs/category/integrations) for further information.
## Create an integration
+
To create an integration, you can choose the supported application from the dropdown at the top right:

@@ -20,19 +22,24 @@ Next, you'll need to fill out the required fields to configure the integration:

### Name
+
Must be at least 1 character long, can be chosen freely.
### Url
+
This must be the base URL of the application where Homarr should connect to.
A few examples for this field include:
+
- http://sabnzbd.example.com
- https://example.com/sabnzbd
You must not append any path after the base URL. Some examples for incorrect usage:
+
- http://sabnzbd.example.com/settings
- https://example.com/sabnzbd/settings
### Secrets
+
An integration may or may not require secrets to function.
To process to obtain them is usually [documented in the integration pages](/docs/category/integrations).
Secrets are encrypted using a symmetric encryption and stored in the database.
@@ -47,6 +54,7 @@ If you already have an application created, you can also link it to the integrat
On the edit page you can always change or remove the linked application.
### Testing and saving integration
+
It is not allowed to create an integration without passing the test.
This is to ensure that you minimize the amount of broken applications on your Homarr instance.
The test and create button will perform a network request to the application and authenticate if required.
@@ -62,6 +70,7 @@ On the edit page of an integration, you can manage which users / groups can

## Troubleshooting connection
+
The connection to your integrations can fail due to many factors. Most of the time it should show you the reason why it failed.
If it doesn't, you can follow these steps to troubleshoot the issue:
@@ -70,23 +79,23 @@ If it doesn't, you can follow these steps to troubleshoot the issue:
- The secrets must be correct and have the correct permissions.
2. If you use https to connect to the application, check the chapter below about [certificate issues](#certificate-issues).
3. Check whether you followed the Homarr documentation and didn't skip any steps.
-This is the most often mistake that create many noisy support requests and following all steps will often resolve the problems.
+ This is the most often mistake that create many noisy support requests and following all steps will often resolve the problems.
4. Check Homarr's logs for more information. As we have no control over breaking changes in upstream APIs and third party software,
-they can break without notice in unexpected ways which can also lead to errors within Homarr. In such case, these occurrences must be reported
-and fixed within the source code of Homarr.
-5. Check whether the host can reach the destination hostname / IP using ``ping`` and ``curl`` or ``wget`` afterward.
-Execute this command on the host machine, not inside containers, to check whether it can connect or not.
+ they can break without notice in unexpected ways which can also lead to errors within Homarr. In such case, these occurrences must be reported
+ and fixed within the source code of Homarr.
+5. Check whether the host can reach the destination hostname / IP using `ping` and `curl` or `wget` afterward.
+ Execute this command on the host machine, not inside containers, to check whether it can connect or not.
6. Check whether the container / pod can reach the destination hostname / IP. Execute the same commands in the shell inside the container / pod.
7. Check your reverse proxy / authentication proxy logs for failures / hints
8. Check your security log of tunnels and VPNs
9. Check your router for security events
10. Check your VLAN configuration and firewall rules
11. Check your DNS settings for the host machine and the containers / pods and whether they can resolve the hostnames
-12. Check throughput and stability of your network connection using ``iperf3``
+12. Check throughput and stability of your network connection using `iperf3`
13. Check syslog / drivers of your OS for hints
14. Check logs of your destination applications (eg. SABnzbd)
15. Attempt to disable CSRF / tamper protections when absolutely no other workaround available -> this comes with some security implications - be careful!
-Re-enable if this didn't fix the issue.
+ Re-enable if this didn't fix the issue.
If none of these troubleshooting steps worked, you can request support from our team in Discord or GitHub.
@@ -95,4 +104,4 @@ If none of these troubleshooting steps worked, you can request support from our
If you are using a self-signed certificate you'll need to add it to the [trusted certificates](../certificates) in Homarr.
Most of the time you should also get a button to add the certificate automatically when you try to connect to the integration.
Please check that the certificate is the one you are expecting to prevent Man in the Middle attacks.
-For some integrations, you can find a guide on how to retrieve the certificate [here](../certificates/#obtaining-certificates).
\ No newline at end of file
+For some integrations, you can find a guide on how to retrieve the certificate [here](../certificates/#obtaining-certificates).
diff --git a/apps/docs/docs/management/mcp.mdx b/apps/docs/docs/management/mcp.mdx
index 540988f29..724a71c0b 100644
--- a/apps/docs/docs/management/mcp.mdx
+++ b/apps/docs/docs/management/mcp.mdx
@@ -96,6 +96,10 @@ MCP supports two authentication methods:
API key authentication is simpler and recommended for most setups. The API key inherits the permissions of the user who created it. Tools that require higher privileges will return permission errors if the key lacks access.
+Custom Widget and Workshop authoring are different: their tools, prompts, and resources are exposed only when the API
+key owner is a Homarr administrator. Non-administrator keys cannot discover or invoke those capabilities. Creating an
+API key never elevates the owner.
+
## Testing the Connection
Verify your MCP endpoint is working:
@@ -124,25 +128,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 |
+| 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, read settings/CSS, 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 |
+| `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.
@@ -152,6 +157,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
@@ -163,3 +170,75 @@ 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 a Custom Widget for this API"** - AI validates and previews one definition before it saves the definition
+
+## Create a Custom Widget with an AI agent
+
+An agent creates one `homarr-custom-widget-v2` definition at a time. Start with the native prompt `homarr-custom-widget-author`.
+
+The API key owner must be a Homarr administrator. The same rule applies to management, import, migration, preview,
+secrets, publication, Workshop installation, and the HTTP authoring resources.
+
+Then, read the live schema and component resources. These resources describe the installed Mantine components.
+
+1. Call `customWidget_getAuthoringPrompt` and `customWidget_schema`.
+2. Build one Custom Widget definition.
+3. Call `customWidget_validate` without saving the definition.
+4. Call `customWidget_previewCreate`.
+5. Test named requests with `customWidget_previewQuery` and `customWidget_previewAction`.
+6. Open the returned preview URL.
+7. Examine `customWidget_previewJournal`.
+8. Revise the local candidate and create another preview session until it is ready.
+9. Call `customWidget_create`, supplying any credentials and deployment-specific source URLs the user already provided.
+10. If the user must enter the source URL or credentials, call `customWidget_configurationRequestUser` with the new definition ID and wait for completion.
+11. Create a final preview with that definition ID so Homarr loads its stored credentials, then test it again.
+12. Use `customWidget_update` or `customWidget_templatePatch` for any final saved-definition corrections.
+13. Repeat this procedure for the next Custom Widget.
+
+The agent receives the configuration-request status. The agent does not receive plaintext credentials. A preview-scoped setup URL configures only that temporary preview; use a definition-scoped URL when the values must persist.
+
+Discovery resources:
+
+- `homarr://custom-widgets/schema`
+- `homarr://custom-widgets/components`
+- `homarr://custom-widgets/components/{name}`
+- `homarr://custom-widgets/examples/{name}`
+- `homarr://custom-widgets/skill`
+
+An authenticated administrator can use the matching `/api/custom-widgets/authoring-prompt`, `/schema`, `/components`,
+and `/skill` endpoints instead of MCP resource URIs. These endpoints are not public schema downloads.
+
+The Custom Widget tools are:
+
+- `customWidget_schema`
+- `customWidget_validate`
+- `customWidget_list`
+- `customWidget_get`
+- `customWidget_create`
+- `customWidget_update`
+- `customWidget_delete`
+- `customWidget_templatePatch`
+- `customWidget_previewCreate`
+- `customWidget_previewQuery`
+- `customWidget_previewAction`
+- `customWidget_previewJournal`
+- `customWidget_secretSet`
+- `customWidget_sourceConfigure`
+- `customWidget_configurationRequestUser`
+- `customWidget_workshopSearch`
+- `customWidget_workshopGet`
+- `customWidget_workshopInstall`
+- `customWidget_getAuthoringPrompt`
+- `customWidget_getSkill`
+
+Custom Widget permissions are not assignable group capabilities. Administrator status is required for the complete
+authoring surface. A source-configuration URL is short-lived and single-use, requires the same logged-in administrator
+to open and complete it, and never returns plaintext credentials to the agent.
+
+Adding a `customApi` item through `board_addItem`, changing its definition or options, and duplicating a board that
+contains one also require an administrator with board-modify access. Existing placements continue to render and run
+authorized requests according to normal board permissions.
+
+Homarr applies its request security controls during request execution. These controls include SSRF, DNS, redirect, size, timeout, rate, and permission controls.
+
+Read [Use agents and skills.sh](./custom-widgets/agent-authoring) for the complete procedure and the portable Agent Skill.
diff --git a/apps/docs/docs/management/media/index.mdx b/apps/docs/docs/management/media/index.mdx
index 090a6559b..e55a05659 100644
--- a/apps/docs/docs/management/media/index.mdx
+++ b/apps/docs/docs/management/media/index.mdx
@@ -7,7 +7,8 @@ tags:
---
# Media
+
Using the media feature, you can upload images for custom icons to Homarr.
Images will be stored in the database. They can be selected in the [icon picker](/docs/advanced/icons).
-
\ No newline at end of file
+
diff --git a/apps/docs/docs/management/search-engines/index.mdx b/apps/docs/docs/management/search-engines/index.mdx
index ef9fba2a3..07f2f3098 100644
--- a/apps/docs/docs/management/search-engines/index.mdx
+++ b/apps/docs/docs/management/search-engines/index.mdx
@@ -6,6 +6,7 @@ tags:
---
# Search Engines
+
Using the search engines, terms can be quickly searched simply by opening the search bar at the top or using the keyboard shortcut `CTRL + K`.

@@ -13,31 +14,36 @@ Using the search engines, terms can be quickly searched simply by opening the se
You can add any search engine that has a search URL that accepts a search term as a parameter.
## Create search engine
+
To create a custom search engine, you can click the button at the top of the page.

### Name
+
The name can be chosen freely. It will be displayed in the search box popover.
### Short
+
Prefix that is used to choose the search engine in the popover using the keyboard.
-It is recommended that this value is kept short (for example ``g`` for Google and ``e`` for Ecosia).
+It is recommended that this value is kept short (for example `g` for Google and `e` for Ecosia).
### Configuration
In the configuration section you can select between two options.
#### Search template
-The template must be a valid URL that incldues the ``%s`` placeholder.
-Homarr will replace the ``%s`` with the term that the user entered.
+
+The template must be a valid URL that incldues the `%s` placeholder.
+Homarr will replace the `%s` with the term that the user entered.
##### Examples
-- Google: ``https://www.google.com/search?q=%s``
-- Ecosia: ``https://www.ecosia.org/search?q=%s``
-- DuckDuckGo: ``https://duckduckgo.com/?q=%s``
-- Bing: ``https://www.bing.com/search?q=%s``
-- YouTube: ``https://www.youtube.com/results?search_query=%s``
+
+- Google: `https://www.google.com/search?q=%s`
+- Ecosia: `https://www.ecosia.org/search?q=%s`
+- DuckDuckGo: `https://duckduckgo.com/?q=%s`
+- Bing: `https://www.bing.com/search?q=%s`
+- YouTube: `https://www.youtube.com/results?search_query=%s`
## Media request search
@@ -45,12 +51,14 @@ Jellyseerr, Overseerr, and Seerr integrations have a dedicated **Spotlight mode*
This mode does not require creating a search engine entry - it is available automatically when you have a compatible integration configured.
You can access it from:
+
- The **Modes list** in Spotlight (opened with `CTRL + K`)
- The **search button** on the media request list and stats widgets
### Availability indicators
Search results display colored status badges showing the media's current state in your integration:
+
- **Available** - fully available in your media library
- **Partial** - some content (e.g. seasons) is available
- **Processing** - currently being downloaded or processed
diff --git a/apps/docs/docs/management/settings/index.mdx b/apps/docs/docs/management/settings/index.mdx
index 016d56d10..8507d2b4f 100644
--- a/apps/docs/docs/management/settings/index.mdx
+++ b/apps/docs/docs/management/settings/index.mdx
@@ -13,6 +13,8 @@ tags:
The settings page applies options to the whole Homarr application.
Some changes need a full page reload before they take effect.
+All changes are collected in a single form. Click the **Save changes** button in the save bar at the bottom of the page to apply them. The save bar only appears when you have unsaved changes, and the **Discard** button reverts your changes.
+
## Analytics
Homarr uses [PostHog](https://posthog.com/) to collect anonymous usage data.
diff --git a/apps/docs/docs/management/tasks/index.mdx b/apps/docs/docs/management/tasks/index.mdx
index bd9395610..c47eb1a74 100644
--- a/apps/docs/docs/management/tasks/index.mdx
+++ b/apps/docs/docs/management/tasks/index.mdx
@@ -38,6 +38,3 @@ You can disable a task by clicking the "Power" button, which will change it to a
When a task is disabled, it will not run automatically according to its schedule.
You can enable it again by clicking the "Power-Off" button, which will change it back to a "Power" button.
Running a task manually is still possible when it is disabled.
-
-
-
diff --git a/apps/docs/docs/management/users/index.mdx b/apps/docs/docs/management/users/index.mdx
index 7be233af0..a7181a1f5 100644
--- a/apps/docs/docs/management/users/index.mdx
+++ b/apps/docs/docs/management/users/index.mdx
@@ -20,8 +20,10 @@ Homarr supports [single sign on for easier and seamless authentication](/docs/ad
:::
## Manage users
+
### Creating new users
-To create new users, you must navigate to ``Mangement`` > ``Users`` > ``Manage`` and click on the create button to create a new one: \
+
+To create new users, you must navigate to `Mangement` > `Users` > `Manage` and click on the create button to create a new one: \

You'll be guided through the process using a wizard: \
@@ -35,25 +37,31 @@ After you completed all steps, you can start over to create another user or choo

## Editing a user
-A user can be edited on the ``Mangement`` > ``Users`` > ``Manage`` page when you click on the username.
+
+A user can be edited on the `Mangement` > `Users` > `Manage` page when you click on the username.
Here you'll have access to two tabs: **General** and **Security**.
### General tab
+
#### Username
+
This is the unique username that identifies this user. It can be changed to anything but must be at least 3 characters long with a limit of 255 characters.
This name will be used for the credential's login. It is also used to auto-generate profile pictures if no picture was uploaded.
#### E-Mail
+
This is the unique email of the user. As of 1.0, it is not used for any notifications and only for organizational purposes.
This field is optional.
#### Language
+
This is the currently selected language of the user.
Changing the language will change all text & the right to left (RTL) feature in some languages.
By default, Homarr will choose English as the default language. Here you can override the language for this specific user.
The user may choose a different language by themselves afterward.
#### Home board
+
Optional field for the board that is loaded by default if no board was specified in the URL.
Only boards that the user has permission to are being shown.
@@ -70,6 +78,7 @@ Choose the first day of the week for the user. This will affect the calendar wid
Option to show icons for status check instead of colored dots.
#### Delete
+
This action will permanently delete the user from the database, including any preferences they set.
If the user created boards, they wil **not** be deleted.
This action is **irreversible** and cannot be undone. All data is permanently lost.
@@ -77,6 +86,7 @@ This action is **irreversible** and cannot be undone. All data is permanently lo
### Security tab
#### Change password
+
Here the password for this user can be changed.
It must also meet the password requirements of Homarr.
The password is stored as an encrypted value with a salt in the database - this means the value cannot be read out from the database afterward.
@@ -85,50 +95,63 @@ This functionality can also be useful to lock out a bad actor without tampering
For security reasons, you must confirm the password a second time.
## Groups
+
Using groups, you can manage the permissions of users.
Members can be directly assigned to groups.
-A user will have the permissions of all groups they are a member of.
+A user will have the permissions of all groups they are a member of.
Some permissions include other permissions, for example the "Full board access" permission includes all board permissions.
By default, a group called "credentials-admin" will be generated where the first user will be placed.
However, you can create as many groups as you'd like.
### Creating a group
-Click on the button ``New group`` to create a new one.
+
+Click on the button `New group` to create a new one.
Homarr will prompt you for a group name.
### Managing a group
+
#### General tab
+
##### Name
+
Name for the group that can be chosen freely.
##### Transfer ownership
+
Every group has an owner. Using this button, the ownership can be transferred to a different user.
Group ownership is only organizational.
##### Delete
+
Deletes the group permanently. This change is irreversible.
#### Settings tab
+
In this tab, you can configure some settings for the group. Those settings will be prioritized by the order of the groups on the groups list page
##### Priorization
+
The higher a group in the list, the higher the priority. This means that if a user is in multiple groups, the permissions of the group with the highest priority will be used.
It will always fallback to the everyone group if no other group permissions are set.

##### Boards
+
Configure board settings for a group. User specific settings will override group settings.
###### Home board
+
The board that is loaded by default if no board was specified in the URL.
###### Mobile board
+
The board that is loaded by default on mobile devices.
#### Members tab
+
In this tab, you can assign users to the group. With the button at the top right, a new member can be added to the group:

@@ -138,6 +161,7 @@ Members can be removed from the group using the remove button at the end:

#### Permissions tab
+
In this tab, you manage what permissions the members of this group will have.
Currently, the following permissions are available:
@@ -153,14 +177,21 @@ Currently, the following permissions are available:
- **Full integration access**: Allow members to manage, use and interact with any integration
- **Administrator**: Members with this permission have full access to all features and settings
+Custom Widget authoring, import, migration, preview, secrets, Workshop access, and placement configuration are reserved
+for administrators and are not separate assignable group permissions. Board modifiers can still move, resize, change
+presentation settings, or remove an existing Custom Widget placement, and board viewers can use placed widgets as
+allowed by each request.
+
When you changed permissions, you must confirm your changes by clicking the banner at the bottom:

#### Special groups
+
Currently one special group exists. This group is `everyone`. It automatically adds every user that is known to Homarr to its members. Neither its name nor its members can be changed.
## Invites
+
Invites are an easy-to-use system to allow users to onboard themselves.
It is useful in companies or large-scale homelabs where many users must be onboarded at the same time.
You should trust the end user that they enter their data truthfully as the administrator will not confirm the entered data after the user has completed the onboarding.
@@ -169,6 +200,7 @@ This feature can only be used when credentials authentication is enabled.
An invitation link can be used indefinitely.
### Creating an invitation
+
Click on the button:

@@ -179,14 +211,15 @@ The default is 4 hours in the future.

-Next, click ``Create`` and you'll get a confirmation with an ID, Token and a link that can be copied:
+Next, click `Create` and you'll get a confirmation with an ID, Token and a link that can be copied:

Both ID and Token are also in the URL. Ensure that you copy either ID & Token or the URL - it will never be shown again.
-Clicking the ``Copy & close`` button will copy the URL to your clipboard.
+Clicking the `Copy & close` button will copy the URL to your clipboard.
### Self-onboarding
+
Clicking the invitation link will bring up a form with fields for the username and password:

@@ -194,6 +227,7 @@ Clicking the invitation link will bring up a form with fields for the username a
At the bottom of the page, the user who created the invitation can be seen.
### Deleting an invitation
+
If an invitation needs to be deleted before or after the expiry, the delete button at the end of the table can be used:

diff --git a/apps/docs/docs/management/workshop/index.mdx b/apps/docs/docs/management/workshop/index.mdx
new file mode 100644
index 000000000..7caffcd42
--- /dev/null
+++ b/apps/docs/docs/management/workshop/index.mdx
@@ -0,0 +1,121 @@
+---
+tags:
+ - Workshop
+ - Custom Widgets
+ - Custom CSS
+---
+
+# Community Workshop
+
+:::warning Experimental feature
+
+This feature is experimental and will probably change. If you encounter bugs, need more capabilities, or find something confusing, tell us in the [official discussion thread](https://github.com/homarr-labs/homarr/discussions/categories/custom-widgets). Your feedback directly shapes what comes next. Happy hacking!
+
+:::
+
+The [Community Workshop](pathname:///workshop) is where Homarr users share and discover
+**custom CSS themes** and **custom widgets**. You can browse submissions, upvote the ones
+you like, report abuse, and install entries directly into your own Homarr instance.
+
+The central `homarr.dev` Workshop is a public community. Browsing it on the website is public. Browsing, installing,
+publishing, or importing Workshop content **inside a Homarr instance is restricted to Homarr administrators**.
+
+:::warning
+
+The Workshop contains community-made content. Moderation is not a security boundary, and PocketBase does not interpret
+or validate submission content. Always review widget capabilities, network origins, actions, and CSS before installing.
+Homarr's installation-time validator is definitive and can reject content that the public service accepted earlier.
+Back up your instance before installing untrusted content.
+
+:::
+
+## Browsing and voting
+
+- Filter by **Widgets** or **CSS**, show or hide outdated entries, and sort by popularity, creation date, recent updates, or discussion activity.
+- Sign in with **GitHub** to upvote/downvote, submit your own creations, or report a submission.
+- Each submission shows its title, description, linked GitHub author, revision, optional screenshots, comments, and full content. Widget pages also summarize API sources, actions, options, network access, and required credentials before installation.
+- A new submission receives one real upvote owned by its author. Votes remain unique per user and submission.
+- Report details and reporter identities are visible only to Workshop moderators. Reported submissions are highlighted in the moderation page, where each open report can be dismissed or the submission can be deleted. Reports do not automatically remove a submission.
+- The manual **Outdated** badge warns visitors that a submission may need an update.
+
+## Comments
+
+Comments are publicly readable and appear in chronological conversation bubbles with the author's avatar and GitHub profile. Signed-in users can post; authors can edit or delete their own comments, and Workshop moderators can delete any comment. Failed posts keep the text you typed. When SMTP and `WORKSHOP_PUBLIC_ORIGIN` are configured, the submission author receives one email for a comment from another user. Self-comments, edits, and deletions do not send mail.
+
+## Submitting
+
+1. Sign in with GitHub.
+2. Click **Share yours** and choose the type:
+ - **Custom widget** — paste the JSON exported from **Manage → Custom Widgets → Export**.
+ It must match the `homarr-custom-widget-v2` schema (validated before submission).
+ - **Custom CSS** — paste your stylesheet (max 16384 characters).
+3. Review the widget JSON or CSS in the syntax-aware editor, then add a title, description, optional screenshots, and changelog.
+4. Submit the entry.
+
+Submissions are published immediately. Reported entries are reviewed and may be removed.
+Authors can edit a submission later to publish a new revision. The edit form can keep or remove existing screenshots and add new PNG, JPG, or WebP images without changing the Workshop URL.
+
+When SMTP and `WORKSHOP_PUBLIC_ORIGIN` are configured, every current Workshop moderator receives an email when a report is submitted. If a moderator deletes the reported submission, its author receives a separate removal email. Author self-deletion does not send a moderation email.
+
+:::info Separate roles
+
+A Workshop moderator is a user with PocketBase's internal `isAdmin` field. This role moderates the central community
+and is unrelated to a Homarr instance administrator.
+
+:::
+
+## Submission guidelines
+
+Submissions must be useful to Homarr users and safe to install. Do not submit content that is
+malicious, deceptive, illegal, hateful, sexually explicit, gore, gambling-focused, spam, or content
+that primarily promotes unrelated products or services.
+
+## Installing into Homarr
+
+You can install workshop entries without leaving Homarr:
+
+- **Custom widgets (Homarr administrators only)** — go to **Manage → Custom Widgets**, or use **Install from Workshop** in the
+ board widget picker. Pick an entry, review its capabilities, and enter any required local secrets before import.
+- **Custom CSS from Workshop (Homarr administrators only)** — open a board's **Settings → Custom CSS**, click **Install from Workshop**, and
+ pick a theme to load it into the editor before saving.
+
+Alternatively, open a submission in the Workshop and use **Download widget JSON** or **Copy CSS**. The submission page shows the matching manual installation steps before the source code.
+
+Custom Widget imports detect required bearer, basic, header, or query credentials. You can enter them during import or explicitly defer them; secret values are never stored in Workshop content. Missing credentials remain visible on the local Custom Widget afterward.
+
+## Workshop deployment
+
+Workshop is powered by [PocketBase](https://pocketbase.io). The production image compiles the current Docusaurus site and serves it together with the API from one origin. The deployment lives in `apps/workshop/` with a Dockerfile, Compose configuration, version-controlled migrations, hooks, a smoke test, and ACT-local workflow instructions.
+
+Run `pnpm docker:workshop` for PocketBase on port 8090, then run
+`WORKSHOP_API_URL=http://127.0.0.1:8090 pnpm dev:docs` for Docusaurus on port 3003. `pnpm docker:docs` builds the
+combined production layout. Normal start and stop commands retain data; `down --volumes` is an intentional,
+irreversible development reset.
+
+GitHub needs only the central callback `https:///api/oauth2-redirect`. Keep PocketBase origins unrestricted
+unless every self-hosted Homarr caller is explicitly listed. Configure both `GITHUB_CLIENT_ID` and
+`GITHUB_CLIENT_SECRET`; supplying only one is a startup error. Set `WORKSHOP_PUBLIC_ORIGIN` in production so generated
+links and email use the public host. Workshop uses the OAuth provider username as the public display name instead of
+the profile's full name. Configure SMTP for comment, report, and removal notices.
+
+The public URL contract is:
+
+| Variable | Purpose | Default |
+| ------------------------ | ------------------------------------------------- | ----------------------------- |
+| `HOMARR_WEBSITE_URL` | Documentation/site base used by Homarr links | `https://homarr.dev` |
+| `WORKSHOP_API_URL` | PocketBase API used by Homarr and standalone docs | `HOMARR_WEBSITE_URL` |
+| `WORKSHOP_WEB_URL` | Public Workshop page linked from Homarr | `HOMARR_WEBSITE_URL/workshop` |
+| `WORKSHOP_PUBLIC_ORIGIN` | PocketBase-generated links and email | Required in production |
+| `PB_ALLOWED_ORIGINS` | PocketBase browser origins | `*` for the central community |
+
+The deprecated docs-only `WORKSHOP_URL` alias remains for one release; `WORKSHOP_API_URL` takes precedence. Restart
+services after changing these values.
+
+Back up application and PocketBase data before deployment. Publish the combined image by immutable digest, test the
+exact digest in staging, rehearse application rollback and PocketBase restore, then promote that same digest.
+
+## Client architecture
+
+Workshop access is centralized in `@homarr/workshop/backend`. It exports the typed `WorkshopBackend`, its typed PocketBase instance, stable TanStack Query option factories, and the `useWorkshop…` query and mutation hooks used by Homarr. The documentation site uses the same backend rather than maintaining a second API contract.
+
+Listing fields derived by the `workshop_listings` view—such as submission type, score, and counts—are normalized by the shared client. Healthy deployments keep filtering, sorting, and pagination in PocketBase. If a persistent development volume still has an older view that rejects those fields, the client retries without them and applies the same operations locally; it can also fall back to the base submissions collection. Production deployments should nevertheless apply the current migration so the view exposes the complete metadata.
diff --git a/apps/docs/docs/widgets/anchor-note/index.mdx b/apps/docs/docs/widgets/anchor-note/index.mdx
index 453a5b51c..522252a1a 100644
--- a/apps/docs/docs/widgets/anchor-note/index.mdx
+++ b/apps/docs/docs/widgets/anchor-note/index.mdx
@@ -1,42 +1,46 @@
---
-title: 'Anchor Note'
-description: 'Display and edit a selected note from Anchor'
+title: "Anchor Note"
+description: "Display and edit a selected note from Anchor"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { anchorNoteWidget } from '.';
-import { anchorIntegration } from '@site/docs/integrations/anchor';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { anchorNoteWidget } from ".";
+import { anchorIntegration } from "@site/docs/integrations/anchor";
-
+
The Anchor Note widget shows a selected note from your Anchor integration. If your note permissions allow it, you can also edit the note title and content directly in Homarr.
### Screenshots
-import anchorNoteWidgetScreenshot from './img/widget.png';
+import anchorNoteWidgetScreenshot from "./img/widget.png";
### Supported Integrations
-
+
### Interactions
-- Click **Edit** to start editing the selected note.
+- Hover the widget, focus within it, or use a touch device to reveal **Edit**, then select it to start editing the note.
- Click **Save** to persist changes to Anchor.
- Click **Cancel** to discard unsaved changes.
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/app/index.mdx b/apps/docs/docs/widgets/app/index.mdx
index ec62d862e..dd5cef590 100644
--- a/apps/docs/docs/widgets/app/index.mdx
+++ b/apps/docs/docs/widgets/app/index.mdx
@@ -1,37 +1,39 @@
---
-title: 'App'
-description: 'Embeds an app into the board.'
+title: "App"
+description: "Embeds an app into the board."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { appWidget } from '.';
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { appWidget } from ".";
-
-
+
The app widget allows you to add a link to an application or a website that you want to access quickly from your Homarr board.
It also supports a status check to indicate whether Homarr can reach the application or website.
### Screenshots
-import githubApp from './img/github.png';
-import jellyseerrApp from './img/jellyseerr-description-tooltip.png';
-import piHoleApp from './img/pi-hole-ping-icon.png';
+import githubApp from "./img/github.png";
+import jellyseerrApp from "./img/jellyseerr-description-tooltip.png";
+import piHoleApp from "./img/pi-hole-ping-icon.png";
-
+
+
+### Interactions
+
+App tiles stay compact and open their configured destination directly. Right-click a tile to inspect its status or change its settings when the board and your preferences allow it.
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/archive-team-warrior/index.mdx b/apps/docs/docs/widgets/archive-team-warrior/index.mdx
new file mode 100644
index 000000000..5eceed2c2
--- /dev/null
+++ b/apps/docs/docs/widgets/archive-team-warrior/index.mdx
@@ -0,0 +1,38 @@
+---
+title: "ArchiveTeam Warrior"
+description: "Monitor ArchiveTeam Warrior status, projects, bandwidth, and active archival jobs."
+hide_title: true
+---
+
+import { archiveTeamWarriorIntegration } from "@site/docs/integrations/archiveteam-warrior";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { archiveTeamWarriorWidget } from ".";
+
+
+
+Use this widget to see what your ArchiveTeam Warrior is preserving without leaving Homarr. The compact view keeps the current project, runner state, job counts, bandwidth, and broadcast message visible. Advanced view adds the complete active-job list and exact update time.
+
+### Supported integration
+
+
+
+
+
+### Adding the widget
+
+
+
+### Configuration
+
+
diff --git a/apps/docs/docs/widgets/archive-team-warrior/index.ts b/apps/docs/docs/widgets/archive-team-warrior/index.ts
new file mode 100644
index 000000000..5a66b9fbb
--- /dev/null
+++ b/apps/docs/docs/widgets/archive-team-warrior/index.ts
@@ -0,0 +1,19 @@
+import type { WidgetDefinition } from "@site/src/types";
+import { IconArchive } from "@tabler/icons-react";
+
+export const archiveTeamWarriorWidget: WidgetDefinition = {
+ icon: IconArchive,
+ name: "ArchiveTeam Warrior",
+ description: "Monitor ArchiveTeam Warrior status, projects, bandwidth, and active archival jobs.",
+ path: "../../widgets/archive-team-warrior",
+ configuration: {
+ items: [
+ {
+ name: "Show broadcast message",
+ description: "Show the message broadcast by the selected Warrior project when space allows.",
+ values: { type: "boolean" },
+ defaultValue: "yes",
+ },
+ ],
+ },
+};
diff --git a/apps/docs/docs/widgets/audio-stats/index.mdx b/apps/docs/docs/widgets/audio-stats/index.mdx
index 88fa87f79..f79a588c3 100644
--- a/apps/docs/docs/widgets/audio-stats/index.mdx
+++ b/apps/docs/docs/widgets/audio-stats/index.mdx
@@ -4,6 +4,8 @@ description: "Displays media and listening statistics from Navidrome or Audioboo
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -36,6 +38,8 @@ Only options relevant to your linked integration are shown in the configuration
]}
/>
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/bazarr/index.mdx b/apps/docs/docs/widgets/bazarr/index.mdx
index e262cfa75..cb35f6c71 100644
--- a/apps/docs/docs/widgets/bazarr/index.mdx
+++ b/apps/docs/docs/widgets/bazarr/index.mdx
@@ -1,26 +1,25 @@
---
-title: 'Bazarr'
-description: 'Displays missing subtitle counts and health indicators from your Bazarr instance.'
+title: "Bazarr"
+description: "Displays missing subtitle counts and health indicators from your Bazarr instance."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { bazarrWidget } from '.';
-import { bazarrIntegration } from '@site/docs/integrations/bazarr';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { bazarrWidget } from ".";
+import { bazarrIntegration } from "@site/docs/integrations/bazarr";
+
+
This widget shows subtitle status from your Bazarr instance, including missing episode and movie subtitles, provider issues, and health warnings.
### Screenshots
-import widgetOverview from './img/widget-overview.png';
+import widgetOverview from "./img/widget-overview.png";
@@ -28,14 +27,21 @@ import widgetOverview from './img/widget-overview.png';
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/beszel-alerts/index.mdx b/apps/docs/docs/widgets/beszel-alerts/index.mdx
index 3a4899d17..d1abe2d2e 100644
--- a/apps/docs/docs/widgets/beszel-alerts/index.mdx
+++ b/apps/docs/docs/widgets/beszel-alerts/index.mdx
@@ -1,22 +1,21 @@
---
-title: 'Beszel Alerts'
-description: 'View Beszel alert configurations and history with triggered/ok status indicators.'
+title: "Beszel Alerts"
+description: "View Beszel alert configurations and history with triggered/ok status indicators."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { beszelAlertsWidget } from '.';
-import { beszelIntegration } from '@site/docs/integrations/beszel';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-import beszelAlerts from './img/beszel-alerts.png';
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { beszelAlertsWidget } from ".";
+import { beszelIntegration } from "@site/docs/integrations/beszel";
-
+import beszelAlerts from "./img/beszel-alerts.png";
+
+
This widget displays your Beszel alert configurations separated by status (triggered and ok). It also shows alert history with timestamps. Alerts are updated in real time via WebSocket subscriptions.
@@ -26,14 +25,21 @@ This widget displays your Beszel alert configurations separated by status (trigg
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/beszel-system-grid/index.mdx b/apps/docs/docs/widgets/beszel-system-grid/index.mdx
index e3b17623b..993974914 100644
--- a/apps/docs/docs/widgets/beszel-system-grid/index.mdx
+++ b/apps/docs/docs/widgets/beszel-system-grid/index.mdx
@@ -1,22 +1,19 @@
---
-title: 'Beszel Systems (Grid)'
-description: 'Card grid view of all Beszel-monitored systems with real-time metrics.'
+title: "Beszel Systems (Grid)"
+description: "Card grid view of all Beszel-monitored systems with real-time metrics."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { beszelSystemGridWidget } from '.';
-import { beszelIntegration } from '@site/docs/integrations/beszel';
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { beszelSystemGridWidget } from ".";
+import { beszelIntegration } from "@site/docs/integrations/beszel";
-import beszelSystemGrid from './img/beszel-system-grid.png';
+import beszelSystemGrid from "./img/beszel-system-grid.png";
-
+
This widget displays all your Beszel-monitored systems as a responsive card grid. Each card shows real-time metrics like CPU, memory, disk, network, temperature, and more. Cards adapt their layout based on available space. If Beszel reports multiple filesystems, the disk bar exposes a hover breakdown for the root disk and each extra mount.
@@ -28,14 +25,19 @@ Select a system card to open the complete statistics view, including system, fil
### Supported Integrations
-
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/beszel-system-stats/index.mdx b/apps/docs/docs/widgets/beszel-system-stats/index.mdx
index e29d083ba..6ac158aac 100644
--- a/apps/docs/docs/widgets/beszel-system-stats/index.mdx
+++ b/apps/docs/docs/widgets/beszel-system-stats/index.mdx
@@ -4,6 +4,8 @@ description: "Time-series charts for CPU, memory, disk, network, and Docker cont
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -32,6 +34,8 @@ This widget displays time-series charts for a selected Beszel system, including
]}
/>
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/beszel-system-table/index.mdx b/apps/docs/docs/widgets/beszel-system-table/index.mdx
index 163d14a2f..b09edf530 100644
--- a/apps/docs/docs/widgets/beszel-system-table/index.mdx
+++ b/apps/docs/docs/widgets/beszel-system-table/index.mdx
@@ -1,25 +1,21 @@
---
-title: 'Beszel Systems (Table)'
-description: 'Table view of all Beszel-monitored systems with sortable columns and status indicators.'
+title: "Beszel Systems (Table)"
+description: "Table view of all Beszel-monitored systems with sortable columns and status indicators."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { beszelSystemTableWidget } from '.';
-import { beszelIntegration } from '@site/docs/integrations/beszel';
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { beszelSystemTableWidget } from ".";
+import { beszelIntegration } from "@site/docs/integrations/beszel";
-
-
+
This widget displays all your Beszel-monitored systems in a sortable data table. Each row shows system metrics like CPU, memory, disk, network, temperature, and more. Columns can be toggled on or off, dragged into a different order, and resized from their headers. Column order and widths are saved automatically for each widget. When Beszel reports extra filesystems, the disk column adds a hover breakdown for each mount alongside the root disk.
-Select a system row to open the complete statistics view, including system, filesystem, Docker, historical, and realtime charts.
+Select a system row to open the complete statistics view.
### Interactions
@@ -30,14 +26,19 @@ Select a system row to open the complete statistics view, including system, file
### Supported Integrations
-
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/bookmarks/index.mdx b/apps/docs/docs/widgets/bookmarks/index.mdx
index c3104ad5b..b61f8f072 100644
--- a/apps/docs/docs/widgets/bookmarks/index.mdx
+++ b/apps/docs/docs/widgets/bookmarks/index.mdx
@@ -1,44 +1,46 @@
---
-title: 'Bookmarks'
-description: 'Displays multiple app links'
+title: "Bookmarks"
+description: "Displays multiple app links"
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { bookmarksWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { bookmarksWidget } from ".";
-
+
The bookmarks widget allows you to display multiple app links in a single widget.
You can add links to your favorite applications, websites, or any other resources you want quick access to.
+Its normal view adapts each configured layout to the available width and height: lower-priority hostnames are hidden before names or icons, and larger collections scroll instead of crushing cards.
### Screenshots
-import gridIconsOnly from './img/grid-icons-only.png';
-import gridWithTitle from './img/grid-with-title.png';
-import verticalListAll from './img/vertical-list-all.png';
-import vericalListSimple from './img/vertical-list-simple.png';
+import gridIconsOnly from "./img/grid-icons-only.png";
+import gridWithTitle from "./img/grid-with-title.png";
+import verticalListAll from "./img/vertical-list-all.png";
+import verticalListSimple from "./img/vertical-list-simple.png";
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/calendar/index.mdx b/apps/docs/docs/widgets/calendar/index.mdx
index fe9df0379..c6b7397a6 100644
--- a/apps/docs/docs/widgets/calendar/index.mdx
+++ b/apps/docs/docs/widgets/calendar/index.mdx
@@ -1,30 +1,28 @@
---
-title: 'Calendar'
-description: 'Display events from your integrations in a calendar view within a certain relative time period'
+title: "Calendar"
+description: "Display events from your integrations in a calendar view within a certain relative time period"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { calendarWidget } from '.';
-import { lidarrIntegration } from '@site/docs/integrations/lidarr';
-import { nextcloudIntegration } from '@site/docs/integrations/nextcloud';
-import { homeAssistantIntegration } from '@site/docs/integrations/home-assistant';
-import { radarrIntegration } from '@site/docs/integrations/radarr';
-import { readarrIntegration } from '@site/docs/integrations/readarr';
-import { sonarrIntegration } from '@site/docs/integrations/sonarr';
-import { icalIntegration } from '@site/docs/integrations/ical';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { calendarWidget } from ".";
+import { lidarrIntegration } from "@site/docs/integrations/lidarr";
+import { nextcloudIntegration } from "@site/docs/integrations/nextcloud";
+import { homeAssistantIntegration } from "@site/docs/integrations/home-assistant";
+import { radarrIntegration } from "@site/docs/integrations/radarr";
+import { readarrIntegration } from "@site/docs/integrations/readarr";
+import { sonarrIntegration } from "@site/docs/integrations/sonarr";
+import { icalIntegration } from "@site/docs/integrations/ical";
-
-
+
The calendar widget shows a month view of events from different integrations.
It allows you to view calendar events or upcoming releases from your media managers.
@@ -33,45 +31,58 @@ the same data is requested through Homarr's MCP or Assistant tools.
### Screenshots
-import popover from './img/popover.png';
-import withIntegrations from './img/with-integrations.png';
-import withoutIntegrations from './img/without-integrations.png';
+import popover from "./img/popover.png";
+import withIntegrations from "./img/with-integrations.png";
+import withoutIntegrations from "./img/without-integrations.png";
-
-
-
-
-
-
+
+
+
+
+
+
### Supported Integrations
-
+
### Interactions
- Hover over a marked date to preview its events in a popover.
-- Click on a marked date to pin the popover open (it will stay visible even after moving the mouse away).
-- Click the date again or click outside to close the pinned popover.
+- The advanced view replaces the month grid with a grouped event list. Use its previous and next controls to change months.
+- If one selected integration is unavailable, its warning indicator remains visible while events from healthy integrations continue to load.
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/clock/index.mdx b/apps/docs/docs/widgets/clock/index.mdx
index efdab31d4..157e79d0b 100644
--- a/apps/docs/docs/widgets/clock/index.mdx
+++ b/apps/docs/docs/widgets/clock/index.mdx
@@ -1,38 +1,38 @@
---
-title: 'Date and time'
-description: 'Displays the current date and time.'
+title: "Date and time"
+description: "Displays the current date and time."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { clockWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { clockWidget } from ".";
-
+
The clock widget displays the current date and time in a customizable format.
### Screenshots
-import localClock from './img/local.png';
-import londonClock from './img/london-with-seconds.png';
-import tokyoClock from './img/tokyo-12-hour.png';
+import localClock from "./img/local.png";
+import londonClock from "./img/london-with-seconds.png";
+import tokyoClock from "./img/tokyo-12-hour.png";
-
-
-
-
+
+
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/coolify/index.mdx b/apps/docs/docs/widgets/coolify/index.mdx
index 23eb6ba03..2feec3a94 100644
--- a/apps/docs/docs/widgets/coolify/index.mdx
+++ b/apps/docs/docs/widgets/coolify/index.mdx
@@ -1,28 +1,33 @@
---
-title: 'Coolify'
-description: 'Overview of your Coolify instance with servers, applications, and services.'
+title: "Coolify"
+description: "Overview of your Coolify instance with servers, applications, and services."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { coolifyWidget } from '.';
-import { coolifyIntegration } from '@site/docs/integrations/coolify';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { coolifyWidget } from ".";
+import { coolifyIntegration } from "@site/docs/integrations/coolify";
+
+
The Coolify widget displays a compact overview of your Coolify instance. It can show servers, applications, and services with status indicators, project and environment context, direct links to Coolify, and the installed Coolify version.
### Supported Integrations
-
+
+
+
### Adding the widget
@@ -34,11 +39,11 @@ The Coolify widget displays a compact overview of your Coolify instance. It can
### Sections
-| Section | Description |
-|---|---|
-| **Servers** | Lists Coolify servers, reachability status, and application/service counts per server. |
+| Section | Description |
+| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
+| **Servers** | Lists Coolify servers, reachability status, and application/service counts per server. |
| **Applications** | Lists applications with runtime status, project and environment context, public URL links, Coolify dashboard links, and log links. |
-| **Services** | Lists services with runtime status, project and environment context, public URL links, Coolify dashboard links, and log links. |
+| **Services** | Lists services with runtime status, project and environment context, public URL links, Coolify dashboard links, and log links. |
### Notes
diff --git a/apps/docs/docs/widgets/custom-api/index.mdx b/apps/docs/docs/widgets/custom-api/index.mdx
index 78d0bcbba..9325a86fe 100644
--- a/apps/docs/docs/widgets/custom-api/index.mdx
+++ b/apps/docs/docs/widgets/custom-api/index.mdx
@@ -1,29 +1,59 @@
---
-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 a custom API endpoint using configurable display types and optional JSX templates."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { customApiWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { customApiWidget } from ".";
-
+
-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.
+A definition contains API sources, named requests, a widget options schema, and one safe JSX template.
-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.
+Temporary inputs exist only while the tile is mounted. Homarr encrypts source credentials separately.
-See the [Custom Widgets documentation](/docs/management/custom-widgets) for full details on creating and configuring custom widgets.
+Homarr does not send these credentials to the browser. Homarr does not include them in an export.
+
+
### Adding the widget
+Select a definition from the searchable picker. Configure its generated widget options.
+
+Select a refresh interval. The board item stores the definition ID and the validated widget option values.
+
+Credentials remain on the server.
+
:::note
Custom API widgets must first be created in **Management → Custom Widgets** before they appear in the widget picker.
+Action buttons can only be executed by administrators.
:::
+
+### Permissions and actions
+
+Each named request declares its minimum board permission. This permission does not depend on the HTTP method.
+
+Queries and actions can use GET, POST, PUT, or PATCH. DELETE is valid only for actions so it can never run
+automatically while a board loads. The `kind` value controls the remaining runtime behavior.
+
+A DELETE request requires full board permission and confirmation. An anonymous viewer cannot run an action.
+
+Homarr disables interactive controls in board edit mode. An action runs only after a user action.
+
+Mounting, refresh polling, and automatic retries do not run actions. An authoring preview simulates actions by default.
+
+An administrator can enable live actions for the short preview session.
+
+### Refresh behavior
+
+The refresh interval runs load queries again. `SubFetch` runs manual queries.
+
+Each query can declare a cache duration. A successful action can invalidate only its declared queries.
+
+Read [Custom Widgets management](/docs/management/custom-widgets) for authoring, widget options, named requests, Mantine, network scopes, and security limits.
diff --git a/apps/docs/docs/widgets/custom-api/index.ts b/apps/docs/docs/widgets/custom-api/index.ts
index c6c56bc53..6b72691d0 100644
--- a/apps/docs/docs/widgets/custom-api/index.ts
+++ b/apps/docs/docs/widgets/custom-api/index.ts
@@ -4,6 +4,7 @@ import { IconApi } from "@tabler/icons-react";
export const customApiWidget: WidgetDefinition = {
icon: IconApi,
name: "Custom API",
- description: "Displays data from a custom API endpoint using configurable display types and optional JSX templates.",
+ description:
+ "Renders an administrator-defined Custom JSX v2 widget with server-side API requests, options, and actions.",
path: "../../widgets/custom-api",
};
diff --git a/apps/docs/docs/widgets/dns-hole-controls/index.mdx b/apps/docs/docs/widgets/dns-hole-controls/index.mdx
index cea517545..9b9d1f26f 100644
--- a/apps/docs/docs/widgets/dns-hole-controls/index.mdx
+++ b/apps/docs/docs/widgets/dns-hole-controls/index.mdx
@@ -1,57 +1,64 @@
---
-title: 'DNS Hole Controls'
-description: "Control PiHole or AdGuard from your dashboard."
+title: "DNS Hole Controls"
+description: "Control Pi-hole, AdGuard Home, or Technitium DNS from your dashboard."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { dnsHoleControlsWidget } from '.';
-import { adguardHomeIntegration } from '@site/docs/integrations/adguard-home';
-import { piHoleIntegration } from '@site/docs/integrations/pi-hole';
-import { technitiumDnsIntegration } from '@site/docs/integrations/technitium-dns';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { dnsHoleControlsWidget } from ".";
+import { adguardHomeIntegration } from "@site/docs/integrations/adguard-home";
+import { piHoleIntegration } from "@site/docs/integrations/pi-hole";
+import { technitiumDnsIntegration } from "@site/docs/integrations/technitium-dns";
+
-
-
-This widget allows you to enable or disable your DNS Hole.
+This widget lets permitted users enable, disable, or temporarily pause one or more DNS services. It shares the summary query and cache with DNS Hole Summary. Every selected source stays available in the normal scrollable view, while cards and controls adapt to the widget's width and height.
### Screenshots
-import bigSize from './img/big.png';
-import smallSize from './img/small.png';
-
+import bigSize from "./img/big.png";
+import smallSize from "./img/small.png";
### Supported Integrations
-
+
### Interactions
-- Enable / Disable one dns-hole by clicking on the badge.
-- Disable one dns-hole in x seconds by clicking on the clock icon.
-- Do all of the above for all dns-holes by using the top button row.
+- Enable or disable one DNS service by clicking its status badge.
+- Temporarily disable one service with the clock action.
+- Apply an action to all available services from the top action row. Homarr keeps successful results visible and reports how many sources failed.
+- Interaction controls only appear when the current user has permission for that integration.
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/dns-hole-summary/index.mdx b/apps/docs/docs/widgets/dns-hole-summary/index.mdx
index 1dcb123da..1e7b966b9 100644
--- a/apps/docs/docs/widgets/dns-hole-summary/index.mdx
+++ b/apps/docs/docs/widgets/dns-hole-summary/index.mdx
@@ -1,38 +1,36 @@
---
-title: 'DNS Hole Summary'
+title: "DNS Hole Summary"
description: "Displays the summary of your DNS Hole."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { dnsHoleSummaryWidget } from '.';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { dnsHoleSummaryWidget } from ".";
+
-
-
-This widget allows you to display statistics about your DNS Hole like total queries, blocked queries, and more.
+This widget combines statistics from every selected DNS service, including total queries, blocked queries, and blocklist size. Its data is cached under the same DNS query family as DNS Hole Controls, so placing both widgets on a board does not create a second integration request. When the widget has enough room, it adds a scrollable status row naming each selected source.
### Screenshots
-import gridLayout from './img/grid.png';
-import verticalLayout from './img/vertical.png';
-import horizontalLayout from './img/horizontal.png';
-import { adguardHomeIntegration } from '@site/docs/integrations/adguard-home';
-import { piHoleIntegration } from '@site/docs/integrations/pi-hole';
-import { technitiumDnsIntegration } from '@site/docs/integrations/technitium-dns';
+import gridLayout from "./img/grid.png";
+import verticalLayout from "./img/vertical.png";
+import horizontalLayout from "./img/horizontal.png";
+import { adguardHomeIntegration } from "@site/docs/integrations/adguard-home";
+import { piHoleIntegration } from "@site/docs/integrations/pi-hole";
+import { technitiumDnsIntegration } from "@site/docs/integrations/technitium-dns";
-
+
-
+
@@ -40,17 +38,26 @@ import { technitiumDnsIntegration } from '@site/docs/integrations/technitium-dns
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/docker-containers/index.mdx b/apps/docs/docs/widgets/docker-containers/index.mdx
index 3dfa40d7f..397520a7e 100644
--- a/apps/docs/docs/widgets/docker-containers/index.mdx
+++ b/apps/docs/docs/widgets/docker-containers/index.mdx
@@ -1,24 +1,22 @@
---
-title: 'Docker stats'
-description: 'Stats of your containers'
+title: "Docker stats"
+description: "Stats of your containers"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { dockerContainersWidget } from '.';
-import { dockerIntegration } from '@site/docs/integrations/docker';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { dockerContainersWidget } from ".";
+import { dockerIntegration } from "@site/docs/integrations/docker";
-
-
+
This widget displays the resource usage of your Docker containers and allows you to manage them.
@@ -28,20 +26,26 @@ This widget can only be used with administrator privileges.
### Screenshots
-import dockerTable from './img/table.png';
+import dockerTable from "./img/table.png";
### Supported Integrations
-
+
### Interactions
Hover a column header to reveal its grab handle, then drag it to reorder the table or drag the header's edge to resize it. The layout is saved automatically for each widget instance and restored after a reload. These controls are disabled while the board is in edit mode.
+Advanced mode exposes every available column regardless of the compact-mode column selection.
+
Each container row has an action menu, accessible via the three-dot icon or by right-clicking the row, with the following options:
- **View logs** - opens a dedicated terminal page streaming the container's logs
@@ -49,6 +53,8 @@ Each container row has an action menu, accessible via the three-dot icon or by r
- **Remove** - permanently remove the container after a second confirmation click
- **Add to Homarr** - create a bookmark app from the container
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/downloads/index.mdx b/apps/docs/docs/widgets/downloads/index.mdx
index d73ad02bb..5d4cca498 100644
--- a/apps/docs/docs/widgets/downloads/index.mdx
+++ b/apps/docs/docs/widgets/downloads/index.mdx
@@ -1,43 +1,41 @@
---
-title: 'Download Client'
-description: 'Allows you to view and manage your Downloads from both Torrent and Usenet clients.'
+title: "Download Client"
+description: "Allows you to view and manage your Downloads from both Torrent and Usenet clients."
hide_title: true
---
-import { aria2Integration } from '@site/docs/integrations/aria2';
-import { delugeIntegration } from '@site/docs/integrations/deluge';
-import { nzbgetIntegration } from '@site/docs/integrations/nzbget';
-import { qBittorentIntegration } from '@site/docs/integrations/q-bittorent';
-import { sabnzbdIntegration } from '@site/docs/integrations/sabnzbd';
-import { transmissionIntegration } from '@site/docs/integrations/transmission';
-import { slskdIntegration } from '@site/docs/integrations/slskd';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { downloadsWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { aria2Integration } from "@site/docs/integrations/aria2";
+import { delugeIntegration } from "@site/docs/integrations/deluge";
+import { nzbgetIntegration } from "@site/docs/integrations/nzbget";
+import { qBittorentIntegration } from "@site/docs/integrations/q-bittorent";
+import { sabnzbdIntegration } from "@site/docs/integrations/sabnzbd";
+import { transmissionIntegration } from "@site/docs/integrations/transmission";
+import { slskdIntegration } from "@site/docs/integrations/slskd";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { downloadsWidget } from ".";
-
+
This widget displays a list of torrents or usenet-articles that are currently being downloaded, along with their progress and other relevant information. It supports 7 download client integrations and provides a rich, responsive table interface.
### Screenshots
-import downloadsCard from './img/screenshot.png';
-import contextMenuImg from './img/context-menu.png';
+import downloadsCard from "./img/screenshot.png";
+import contextMenuImg from "./img/context-menu.png";
-
+
#### Right-click context menu
-
+
### Features
@@ -56,28 +54,38 @@ import contextMenuImg from './img/context-menu.png';
### Supported Integrations
-
+
### Interactions
@@ -89,9 +97,12 @@ import contextMenuImg from './img/context-menu.png';
- **Filter** by integration or status using the filter button in the footer
- **Sort** by clicking column headers (with persistent sort direction)
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/firewall/index.mdx b/apps/docs/docs/widgets/firewall/index.mdx
index 95aaa56d6..aec2d89d9 100644
--- a/apps/docs/docs/widgets/firewall/index.mdx
+++ b/apps/docs/docs/widgets/firewall/index.mdx
@@ -1,22 +1,20 @@
---
-title: 'Firewall Monitoring'
-description: 'Displays a summary of firewalls.'
+title: "Firewall Monitoring"
+description: "Displays a summary of firewalls."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { firewallWidget } from '.';
-import { opnsenseIntegration } from '../../integrations/opnsense';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { firewallWidget } from ".";
+import { opnsenseIntegration } from "../../integrations/opnsense";
-
+
### Screenshots
@@ -24,9 +22,15 @@ import { opnsenseIntegration } from '../../integrations/opnsense';
### Supported Integrations
-
+
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/health-monitoring/index.mdx b/apps/docs/docs/widgets/health-monitoring/index.mdx
index 603207451..bc50cf2c7 100644
--- a/apps/docs/docs/widgets/health-monitoring/index.mdx
+++ b/apps/docs/docs/widgets/health-monitoring/index.mdx
@@ -4,6 +4,8 @@ description: "Displays information showing the health and status of your system(
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -62,6 +64,8 @@ import { synologyIntegration } from "@site/docs/integrations/synology";
]}
/>
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/iframe/index.mdx b/apps/docs/docs/widgets/iframe/index.mdx
index 5be26f3b2..5f410a7f3 100644
--- a/apps/docs/docs/widgets/iframe/index.mdx
+++ b/apps/docs/docs/widgets/iframe/index.mdx
@@ -1,19 +1,17 @@
---
-title: 'iFrame'
-description: 'Embed any content from the internet. Some websites may restrict access.'
+title: "iFrame"
+description: "Embed any content from the internet. Some websites may restrict access."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { iframeWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { iframeWidget } from ".";
-
+
If your application is not supported natively by Homarr, you can integrate it using the iframe widget.
It uses the [iframe element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe),
@@ -22,14 +20,16 @@ This means, no traffic is routed through Homarr (and the URL must be reachable f
### Screenshots
-import wikipediaHomepage from './img/wikipedia.png';
-import youtubeStream from './img/nasa-iss-stream.png';
+import wikipediaHomepage from "./img/wikipedia.png";
+import youtubeStream from "./img/nasa-iss-stream.png";
+
+
### Adding the widget
@@ -39,26 +39,27 @@ import youtubeStream from './img/nasa-iss-stream.png';
Although you can integrate many websites, some may not work.
This is because the administrators of those sites disallow iframes
for security reasons, as users could easily be tricked
-by embeding the original site on a malicious page.
+by embedding the original site on a malicious page.
Some proxies disallow iframes by default for this reason.
-You can use https://iframetest.com to check, if the site is allowing iframes.
+You can use https://iframetest.com to check whether the site allows iframes.
If you manage all of your own applications, you can usually change that by configuring
-the appropriate headers (such as `X-Frame-Options` or `Content-Security-Policy`) to allow iframes.
+the `Content-Security-Policy` header and its `frame-ancestors` directive. The obsolete
+`X-Frame-Options: ALLOW-FROM` directive is not supported by modern browsers.
For example, to allow your site to be embedded in iframes from specific domains:
**Apache (.htaccess or httpd.conf):**
+
```apache
Header set Content-Security-Policy "frame-ancestors 'self' https://trusted-domain.com"
-Header set X-Frame-Options "ALLOW-FROM https://trusted-domain.com"
```
**Nginx:**
+
```nginx
add_header Content-Security-Policy "frame-ancestors 'self' https://trusted-domain.com";
-add_header X-Frame-Options "ALLOW-FROM https://trusted-domain.com";
```
### Example URLs
@@ -67,6 +68,7 @@ add_header X-Frame-Options "ALLOW-FROM https://trusted-domain.com";
- **Any YouTube video or stream:** https://www.youtube.com/embed/dQw4w9WgXcQ?si=MWUbbG66UNTla_Ni
### Configuration
+
### Transparent iframes
@@ -75,28 +77,30 @@ To create a transparent iframe like below you can use the following html code an
```html
-
+
-
+
```

-### Third party content
+### Third-party content
+
:::warning
-The content below is an overview of third party projects. All support requests/problems must be redirected to the appropriate repository.
+The content below is an overview of third-party projects. All support requests/problems must be redirected to the appropriate repository.
We cannot guarantee the functionality nor security of these tools.
:::
### homarr-iframes from Diogo Valentim
+
The project [homarr-iframes](https://github.com/diogovalentte/homarr-iframes) has iframes designed to be used with Homarr, these iframes offer a minimalist view of other systems:
- [Vikunja](https://github.com/go-vikunja/vikunja): overview of your tasks with links to them.
- [Linkwarden](https://github.com/linkwarden/linkwarden): overview of your bookmarks with links to them.
-- [Cinemark Brazil](https://www.cinemark.com.br): overview of the movies in Cinemark theaters in your city (*only in Brazil*).
+- [Cinemark Brazil](https://www.cinemark.com.br): overview of the movies in Cinemark theaters in your city (_only in Brazil_).
, Linkwarden (middle), and Cinemark Brazil (right) iframes on a Homarr dashboard")
diff --git a/apps/docs/docs/widgets/immich-album-carousel/index.mdx b/apps/docs/docs/widgets/immich-album-carousel/index.mdx
index 703bd5a57..4a0cc6eef 100644
--- a/apps/docs/docs/widgets/immich-album-carousel/index.mdx
+++ b/apps/docs/docs/widgets/immich-album-carousel/index.mdx
@@ -1,39 +1,44 @@
---
-title: 'Immich Album Carousel'
-description: 'Shows a slideshow of pictures from an Immich album or your full library'
+title: "Immich Album Carousel"
+description: "Shows a slideshow of pictures from an Immich album or your full library"
hide_title: true
---
-import { immichIntegration } from '../../integrations/immich';import {
- AddingWidget
-} from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { immichAlbumCarouselWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { immichIntegration } from "../../integrations/immich";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { immichAlbumCarouselWidget } from ".";
-
+
### Screenshots
-import screen from './img/screen.png';
+import screen from "./img/screen.png";
### Supported Integrations
-
+
+
+
+
+Hover or focus the widget to pause its slideshow; touch devices keep the control available. A paused slideshow keeps its resume control visible. The advanced view groups previous, pause or resume, and next together and adds a thumbnail strip for quick navigation.
### Adding the widget
-
### Configuration
+
diff --git a/apps/docs/docs/widgets/immich-server-stats/index.mdx b/apps/docs/docs/widgets/immich-server-stats/index.mdx
index 73d278a91..e2c967365 100644
--- a/apps/docs/docs/widgets/immich-server-stats/index.mdx
+++ b/apps/docs/docs/widgets/immich-server-stats/index.mdx
@@ -1,40 +1,44 @@
---
-title: 'Immich Server Stats'
-description: 'Information about your Immich instance'
+title: "Immich Server Stats"
+description: "Information about your Immich instance"
hide_title: true
---
-import { immichIntegration } from '../../integrations/immich';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { immichServerStatsWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { immichIntegration } from "../../integrations/immich";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { immichServerStatsWidget } from ".";
-
+
### Screenshots
-import screen from './img/screen.png';
-
+import screen from "./img/screen.png";
### Supported Integrations
-
+
+
+In advanced mode, the widget also ranks the largest albums by asset count. To keep the dashboard response lightweight, it shows up to 50 albums.
+
+
### Adding the widget
-
### Configuration
-
+
diff --git a/apps/docs/docs/widgets/indexer-manager/index.mdx b/apps/docs/docs/widgets/indexer-manager/index.mdx
index fad8c341b..2deeecd66 100644
--- a/apps/docs/docs/widgets/indexer-manager/index.mdx
+++ b/apps/docs/docs/widgets/indexer-manager/index.mdx
@@ -1,47 +1,50 @@
---
-title: 'Indexer manager status'
-description: 'View the status of your indexers.'
+title: "Indexer manager status"
+description: "View the status of your indexers."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { indexerManagerWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { indexerManagerWidget } from ".";
-
+
### Screenshots
-import bigItem from './img/big.png';
-import smallItem from './img/small.png';
-import { prowlarrIntegration } from '@site/docs/integrations/prowlarr';
+import bigItem from "./img/big.png";
+import smallItem from "./img/small.png";
+import { prowlarrIntegration } from "@site/docs/integrations/prowlarr";
-
+
### Supported Integrations
-
+
### Interactions
- Test all indexers by clicking on the test button.
+
+
### Adding the widget
-
### Configuration
-
+
diff --git a/apps/docs/docs/widgets/media-missing/index.mdx b/apps/docs/docs/widgets/media-missing/index.mdx
index 648bd4190..643da6a3c 100644
--- a/apps/docs/docs/widgets/media-missing/index.mdx
+++ b/apps/docs/docs/widgets/media-missing/index.mdx
@@ -1,29 +1,28 @@
---
-title: 'Media Missing & Queue'
-description: 'Track missing media and active downloads from Radarr and Sonarr'
+title: "Media Missing & Queue"
+description: "Track missing media and active downloads from Radarr and Sonarr"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { mediaMissingWidget } from '.';
-import { radarrIntegration } from '@site/docs/integrations/radarr';
-import { sonarrIntegration } from '@site/docs/integrations/sonarr';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { mediaMissingWidget } from ".";
+import { radarrIntegration } from "@site/docs/integrations/radarr";
+import { sonarrIntegration } from "@site/docs/integrations/sonarr";
+
+
Monitor which movies and episodes are missing from your library, and track what is currently being downloaded. The widget pulls data from Radarr and Sonarr and groups results by type in two tabs.
### Screenshots
-import widgetMissing from './img/missing.png';
-import widgetQueued from './img/queued.png';
-import widgetCompact from './img/compact.png';
+import widgetMissing from "./img/missing.png";
+import widgetQueued from "./img/queued.png";
+import widgetCompact from "./img/compact.png";
@@ -33,18 +32,23 @@ import widgetCompact from './img/compact.png';
### Supported Integrations
-
+
### Tabs
-| Tab | Description |
-|---|---|
+| Tab | Description |
+| ----------- | ----------------------------------------------------------------------------------------------------- |
| **Missing** | Lists movies (Radarr) and episodes (Sonarr) that are monitored but not yet available in your library. |
-| **Queued** | Shows items currently being downloaded with a progress bar and estimated time remaining. |
+| **Queued** | Shows items currently being downloaded with a progress bar and estimated time remaining. |
### Sizing
@@ -56,6 +60,8 @@ The widget adapts to its container size, so it looks good at any grid size:
Each item shows its poster art (with the artwork bleeding softly behind the card), a type badge, and — on the queue — an animated progress bar with the estimated time remaining.
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/media-releases/index.mdx b/apps/docs/docs/widgets/media-releases/index.mdx
index fece199eb..14695f0fe 100644
--- a/apps/docs/docs/widgets/media-releases/index.mdx
+++ b/apps/docs/docs/widgets/media-releases/index.mdx
@@ -1,30 +1,28 @@
---
-title: 'Media releases'
-description: 'Display newly added medias or upcoming releases from different integrations'
+title: "Media releases"
+description: "Display newly added media or upcoming releases from different integrations"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { mediaReleasesWidget } from '.';
-import { embyIntegration } from '@site/docs/integrations/emby';
-import { jellyfinIntegration } from '@site/docs/integrations/jellyfin';
-import { plexIntegration } from '@site/docs/integrations/plex';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { mediaReleasesWidget } from ".";
+import { embyIntegration } from "@site/docs/integrations/emby";
+import { jellyfinIntegration } from "@site/docs/integrations/jellyfin";
+import { plexIntegration } from "@site/docs/integrations/plex";
-
+
Provides a list of newly added media or upcoming releases from your media server.
### Screenshots
-import backdrop from './img/backdrop.png';
-import poster from './img/poster.png';
+import backdrop from "./img/backdrop.png";
+import poster from "./img/poster.png";
@@ -33,17 +31,26 @@ import poster from './img/poster.png';
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/media-request-list/index.mdx b/apps/docs/docs/widgets/media-request-list/index.mdx
index 1c491a051..840315831 100644
--- a/apps/docs/docs/widgets/media-request-list/index.mdx
+++ b/apps/docs/docs/widgets/media-request-list/index.mdx
@@ -4,6 +4,8 @@ description: "See a list of all media requests from your integration"
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -44,6 +46,9 @@ import mediaRequestList from "./img/list.png";
- Approve / decline a request by clicking on the thumbs up or down icon.
- Hover over the widget to reveal a **search button** that opens Spotlight in media request search mode, scoped to the widget's integrations.
+- A warning indicator identifies unavailable integrations without hiding requests returned by healthy integrations.
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/media-request-stats/index.mdx b/apps/docs/docs/widgets/media-request-stats/index.mdx
index a7b40f624..3adfcbffb 100644
--- a/apps/docs/docs/widgets/media-request-stats/index.mdx
+++ b/apps/docs/docs/widgets/media-request-stats/index.mdx
@@ -1,45 +1,51 @@
---
-title: 'Media Requests Stats'
-description: 'Statistics about your media requests'
+title: "Media Requests Stats"
+description: "Statistics about your media requests"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { mediaRequestStatsWidget } from '.';
-import { jellyseerrIntegration } from '@site/docs/integrations/jellyseerr';
-import { overseerrIntegration } from '@site/docs/integrations/overseerr';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { mediaRequestStatsWidget } from ".";
+import { jellyseerrIntegration } from "@site/docs/integrations/jellyseerr";
+import { overseerrIntegration } from "@site/docs/integrations/overseerr";
+
-
-
-Provide a statistics overview of your media requests, such as mount of requests, open requests, approvement and rejections.
+Provides an overview of media requests, including totals, availability, approvals, and rejections. Resize the widget to reveal denser stat tiles; the user ranking remains scrollable instead of being truncated.
### Supported Integrations
-
+
### Screenshots
-import mediaRequestStats from './img/stats.png';
+import mediaRequestStats from "./img/stats.png";
### Interactions
- Hover over the widget to reveal a **search button** that opens Spotlight in media request search mode, scoped to the widget's integrations.
+- A warning indicator identifies unavailable integrations while totals from healthy integrations remain visible.
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/media-server/index.mdx b/apps/docs/docs/widgets/media-server/index.mdx
index 8fb0c857c..d0acb93b2 100644
--- a/apps/docs/docs/widgets/media-server/index.mdx
+++ b/apps/docs/docs/widgets/media-server/index.mdx
@@ -1,56 +1,66 @@
---
-title: 'Media server streams'
-description: 'Show the current streams on your media servers'
+title: "Media server streams"
+description: "Show the current streams on your media servers"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { mediaServerWidget } from '.';
-import { embyIntegration } from '@site/docs/integrations/emby';
-import { jellyfinIntegration } from '@site/docs/integrations/jellyfin';
-import { navidromeIntegration } from '@site/docs/integrations/navidrome';
-import { plexIntegration } from '@site/docs/integrations/plex';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { mediaServerWidget } from ".";
+import { embyIntegration } from "@site/docs/integrations/emby";
+import { jellyfinIntegration } from "@site/docs/integrations/jellyfin";
+import { navidromeIntegration } from "@site/docs/integrations/navidrome";
+import { plexIntegration } from "@site/docs/integrations/plex";
+
-
-
-The media server widget enables you to watch and view current active sessions over all your media servers.
-It also displays useful information, that you can use when providing support to users or debugging a problem.
-
+The media server widget shows active sessions across your media servers.
+It shows playback progress, pause state, time remaining, and whether each session is direct play, direct stream, or transcoding audio or video. Location and bitrate can be toggled independently.
+Select a session to inspect its media metadata. Advanced mode adds search, sorting, and a summary of playing and transcoding sessions while keeping separate integration instances identifiable.
+If one selected server is unavailable, a warning indicator identifies the degraded result while sessions from healthy servers remain usable.
+Sessions without user metadata remain visible and are labeled as an unknown user.
### Screenshots
-import mediaTable from './img/table.png';
+import mediaTable from "./img/table.png";
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/media-transcoding/index.mdx b/apps/docs/docs/widgets/media-transcoding/index.mdx
index 0fc280021..256b8edd6 100644
--- a/apps/docs/docs/widgets/media-transcoding/index.mdx
+++ b/apps/docs/docs/widgets/media-transcoding/index.mdx
@@ -1,35 +1,31 @@
---
-title: 'Media transcoding'
-description: 'Statistics, current queue and worker status of your media transcoding'
+title: "Media transcoding"
+description: "Statistics, current queue and worker status of your media transcoding"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { mediaTranscodingWidget } from '.';
-import { tdarrIntegration } from '@site/docs/integrations/tdarr';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { mediaTranscodingWidget } from ".";
+import { tdarrIntegration } from "@site/docs/integrations/tdarr";
+
-
-
-The media transcoding widget provides you with an overview of your media transcoding status.
+The media transcoding widget provides you with an overview of your media transcoding status.
It shows you the current queue, worker status, and statistics about your transcoding jobs.
-
-
### Screenshots
-import queueCard from './img/queue.png';
-import workersCard from './img/workers.png';
-import statisticsCard from './img/statistics.png';
+import queueCard from "./img/queue.png";
+import workersCard from "./img/workers.png";
+import statisticsCard from "./img/statistics.png";
@@ -39,13 +35,20 @@ import statisticsCard from './img/statistics.png';
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/minecraft-server-status/index.mdx b/apps/docs/docs/widgets/minecraft-server-status/index.mdx
index 51101246b..4df364116 100644
--- a/apps/docs/docs/widgets/minecraft-server-status/index.mdx
+++ b/apps/docs/docs/widgets/minecraft-server-status/index.mdx
@@ -1,44 +1,45 @@
---
-title: 'Minecraft Server Status'
-description: 'Displays the status of a Minecraft server.'
+title: "Minecraft Server Status"
+description: "Displays the status of a Minecraft server."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import TabItem from '@theme/TabItem';
-import Tabs from '@theme/Tabs';
-import { minecraftServerStatusWidget } from '.';
+import TabItem from "@theme/TabItem";
+import Tabs from "@theme/Tabs";
+import { minecraftServerStatusWidget } from ".";
-
-
+
Shows the status and amount of players on a Minecraft server. It supports both Java and Bedrock editions, however only public servers are supported which can be reached through the used api of [api.mcsrvstat.us](https://api.mcsrvstat.us).
### Screenshots
-import javaItem from './img/java.png';
-import bedrockItem from './img/bedrock.png';
+import javaItem from "./img/java.png";
+import bedrockItem from "./img/bedrock.png";
-
+
-
+
+
+
### Adding the widget
### Configuration
+
### Data source
We use the Minecraft Server Status API to fetch the server status.
-If you need to whitelist the api, please use the following URL: `hhttps://api.mcsrvstat.us`
\ No newline at end of file
+If you need to whitelist the API, use the following URL: `https://api.mcsrvstat.us`.
diff --git a/apps/docs/docs/widgets/network-controller-status/index.mdx b/apps/docs/docs/widgets/network-controller-status/index.mdx
index a01496234..ad0feaf9d 100644
--- a/apps/docs/docs/widgets/network-controller-status/index.mdx
+++ b/apps/docs/docs/widgets/network-controller-status/index.mdx
@@ -1,44 +1,49 @@
---
-title: 'Network Status'
-description: 'Display connected devices on a network'
+title: "Network Status"
+description: "Display connected devices on a network"
hide_title: true
---
-import { unifiControllerIntegration } from '@site/docs/integrations/unifi-controller';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { unifiControllerIntegration } from "@site/docs/integrations/unifi-controller";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import TabItem from '@theme/TabItem';
-import Tabs from '@theme/Tabs';
-import { networkControllerStatusWidget } from '.';
+import TabItem from "@theme/TabItem";
+import Tabs from "@theme/Tabs";
+import { networkControllerStatusWidget } from ".";
-
-
+
Shows connected users and connected guest devices on wired or wireless networks.
### Screenshots
-import wifiCard from './img/wifi.png';
-import wiredCard from './img/wired.png';
+import wifiCard from "./img/wifi.png";
+import wiredCard from "./img/wired.png";
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/network-controller-summary/index.mdx b/apps/docs/docs/widgets/network-controller-summary/index.mdx
index fe4cd3357..a71c9233a 100644
--- a/apps/docs/docs/widgets/network-controller-summary/index.mdx
+++ b/apps/docs/docs/widgets/network-controller-summary/index.mdx
@@ -1,38 +1,42 @@
---
-title: 'Network Controller Summary'
-description: 'Displays the summary of a Network Controller'
+title: "Network Controller Summary"
+description: "Displays the summary of a Network Controller"
hide_title: true
---
-import { unifiControllerIntegration } from '@site/docs/integrations/unifi-controller';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { unifiControllerIntegration } from "@site/docs/integrations/unifi-controller";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import TabItem from '@theme/TabItem';
-import Tabs from '@theme/Tabs';
-import { networkControllerSummaryWidget } from '.';
+import TabItem from "@theme/TabItem";
+import Tabs from "@theme/Tabs";
+import { networkControllerSummaryWidget } from ".";
+
-
-
-Shows information whether your WAN is connected, Wi-Fi is active and several other information.
+Shows whether your WAN, internet, Wi-Fi, LAN, and VPN are available. Wider or taller layouts progressively reveal latency, ping, and connected-client counts without a separate view.
### Screenshots
-import summaryCard from './img/summary.png';
+import summaryCard from "./img/summary.png";
### Supported Integrations
-
+
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/notebook/index.mdx b/apps/docs/docs/widgets/notebook/index.mdx
index 393137ed2..887908372 100644
--- a/apps/docs/docs/widgets/notebook/index.mdx
+++ b/apps/docs/docs/widgets/notebook/index.mdx
@@ -4,6 +4,8 @@ description: "A simple notebook widget that supports markdown."
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
import { WidgetHeader } from "@site/src/components/widgets/header";
@@ -24,6 +26,8 @@ import editOn from "./img/edit.png";
+
+
### Adding the widget
@@ -34,7 +38,7 @@ import editOn from "./img/edit.png";
### Editing
-When the permissions of the user allows it, you can enter the editing of the text by clicking the following button in the top right.
+When your permissions allow it, hover the widget, focus within it, or use a touch device to reveal the edit button in the top right. You can also double-click the document to edit it directly.

diff --git a/apps/docs/docs/widgets/notifications/index.mdx b/apps/docs/docs/widgets/notifications/index.mdx
index 0a715ab3c..89771e35b 100644
--- a/apps/docs/docs/widgets/notifications/index.mdx
+++ b/apps/docs/docs/widgets/notifications/index.mdx
@@ -1,44 +1,41 @@
---
-title: 'Notifications'
-description: 'Display notification history from an integration'
+title: "Notifications"
+description: "Display notification history from an integration"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { notificationsWidget } from '.';
-import { ntfyIntegration } from '@site/docs/integrations/ntfy';
-import { nextcloudIntegration } from '@site/docs/integrations/nextcloud';
-import { gotifyIntegration } from '@site/docs/integrations/gotify';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { IconAd } from "@tabler/icons-react";
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { notificationsWidget } from ".";
+import { ntfyIntegration } from "@site/docs/integrations/ntfy";
+import { nextcloudIntegration } from "@site/docs/integrations/nextcloud";
+import { gotifyIntegration } from "@site/docs/integrations/gotify";
+
+
The notifications widget allows you to display notifications from various integrations in a unified view.
### Screenshots
-import notificationsScreenshot from './img/notifications.png';
+import notificationsScreenshot from "./img/notifications.png";
### Supported Integrations
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/paperless-ngx/index.mdx b/apps/docs/docs/widgets/paperless-ngx/index.mdx
index 3dd684efd..a2ef00ebe 100644
--- a/apps/docs/docs/widgets/paperless-ngx/index.mdx
+++ b/apps/docs/docs/widgets/paperless-ngx/index.mdx
@@ -1,34 +1,39 @@
---
-title: 'Paperless-ngx'
-description: 'Displays document management statistics including inbox ratio, document counts, and metadata.'
+title: "Paperless-ngx"
+description: "Displays document management statistics including inbox ratio, document counts, and metadata."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { paperlessNgxWidget } from '.';
-import { paperlessNgxIntegration } from '@site/docs/integrations/paperless-ngx';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { paperlessNgxWidget } from ".";
+import { paperlessNgxIntegration } from "@site/docs/integrations/paperless-ngx";
-
+
This widget displays statistics from your Paperless-ngx instance, including an inbox ratio ring showing how many documents are in your inbox versus total, along with counts for correspondents, tags, and document types.
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/patchmon/index.mdx b/apps/docs/docs/widgets/patchmon/index.mdx
index d16ab8d68..90bb6754c 100644
--- a/apps/docs/docs/widgets/patchmon/index.mdx
+++ b/apps/docs/docs/widgets/patchmon/index.mdx
@@ -1,21 +1,19 @@
---
-title: 'PatchMon'
-description: 'Displays host patch statistics from PatchMon including update and security counts.'
+title: "PatchMon"
+description: "Displays host patch statistics from PatchMon including update and security counts."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { patchmonWidget } from '.';
-import { patchmonIntegration } from '@site/docs/integrations/patchmon';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { patchmonWidget } from ".";
+import { patchmonIntegration } from "@site/docs/integrations/patchmon";
-
+
This widget displays patch statistics from your PatchMon instance, including total hosts, hosts needing updates, and security update counts. Each stat is shown with a dedicated icon and severity-tinted card background for quick visual scanning.
@@ -39,13 +37,13 @@ In **1-column-wide** layouts (such as 1×1), the compliance hero switches to a r
When **Enable threshold colors** is on (default), stat values and card backgrounds are colored for at-a-glance severity:
-| Stat | Green | Yellow | Red |
-|------|-------|--------|-----|
-| Needs Updates | 0 | 1–4 | 5+ |
-| Security Updates | 0 | 1–9 | 10+ |
-| Hosts with Security Updates | 0 | — | 1+ |
-| Up-to-Date Hosts | 100% of total | 90–99% | below 90% |
-| Outdated Packages | 0 | 1–49 | 50+ |
+| Stat | Green | Yellow | Red |
+| --------------------------- | ------------- | ------ | --------- |
+| Needs Updates | 0 | 1–4 | 5+ |
+| Security Updates | 0 | 1–9 | 10+ |
+| Hosts with Security Updates | 0 | — | 1+ |
+| Up-to-Date Hosts | 100% of total | 90–99% | below 90% |
+| Outdated Packages | 0 | 1–49 | 50+ |
Enable **Advanced thresholds** to override warning and critical values per stat, using either absolute counts or percentages of total hosts.
@@ -62,13 +60,13 @@ The OS distribution limit defaults to **All**. A widget size of **2×3** or larg
The widget adapts its layout based on the available space:
-| Widget size | Layout |
-|-------------|--------|
-| **1×1** | Primary selected content: compliance ring (no label), compact OS distribution, or up to two enabled stats |
+| Widget size | Layout |
+| ----------------- | ------------------------------------------------------------------------------------------------------------------- |
+| **1×1** | Primary selected content: compliance ring (no label), compact OS distribution, or up to two enabled stats |
| **2×1** / **1×2** | Compact primary content; **2×1** shows compliance label and host ratio beside the ring; **1×2** uses ring-only hero |
-| **2×2** (default) | Compliance hero, stat grid with icons, and tinted cards |
-| **2×3** / **3×2** | Hero, stats, OS distribution, and "last updated" footer |
-| **3×3** or larger | Full layout with up to 4-column stat grid, donut chart, and optional OS legend |
+| **2×2** (default) | Compliance hero, stat grid with icons, and tinted cards |
+| **2×3** / **3×2** | Hero, stats, OS distribution, and "last updated" footer |
+| **3×3** or larger | Full layout with up to 4-column stat grid, donut chart, and optional OS legend |
### Last updated
@@ -76,14 +74,21 @@ When the widget has enough space, a "last updated" timestamp appears at the bott
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/releases/index.mdx b/apps/docs/docs/widgets/releases/index.mdx
index faf386a13..38134344a 100644
--- a/apps/docs/docs/widgets/releases/index.mdx
+++ b/apps/docs/docs/widgets/releases/index.mdx
@@ -4,6 +4,8 @@ description: "Displays a list of the current version of the given repositories w
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
import { WidgetHeader } from "@site/src/components/widgets/header";
@@ -33,6 +35,8 @@ GitHub, Docker Hub, GitLab, NPM, Codeberg, GitHub Container Registry, Quay, and
For GitHub Container Registry, use `owner/package` identifiers such as `homarr-labs/homarr`.
Full image references such as `ghcr.io/homarr-labs/homarr:latest` are also accepted.
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/rss-feed/index.mdx b/apps/docs/docs/widgets/rss-feed/index.mdx
index 68bbb1071..6fe5325de 100644
--- a/apps/docs/docs/widgets/rss-feed/index.mdx
+++ b/apps/docs/docs/widgets/rss-feed/index.mdx
@@ -1,37 +1,42 @@
---
-title: 'RSS Feed'
-description: 'Monitor and display one or more generic RSS, ATOM or JSON feeds.'
+title: "RSS Feed"
+description: "Monitor and display one or more generic RSS, ATOM or JSON feeds."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { rssFeedWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { rssFeedWidget } from ".";
-
+
Using the RSS standard, you can integrate most News websites, applications and more on your dashboard.
### Screenshots
-import screenshot from './img/screenshot.png';
+import screenshot from "./img/screenshot.png";
+
+
### Adding the widget
### Configuration
+
+If one configured feed fails, the widget keeps entries from healthy feeds visible and shows a warning. If a refresh fails after entries have loaded, the previous entries stay visible with a stale-data warning.
+
### Examples
+
Here's a small collection of examples, that you can use for testing or actual use (in no particular order):
+
- https://noted.lol/rss/
- https://blog.google/products/chrome/rss/
- https://www.w3.org/blog/feed/
@@ -45,4 +50,4 @@ Here's a small collection of examples, that you can use for testing or actual us
- http://feeds.foxnews.com/foxnews/latest
There is a good list of feeds here:
-https://github.com/greysonp/rss-feeds/blob/master/feeds.json
\ No newline at end of file
+https://github.com/greysonp/rss-feeds/blob/master/feeds.json
diff --git a/apps/docs/docs/widgets/smart-home-entity-state/index.mdx b/apps/docs/docs/widgets/smart-home-entity-state/index.mdx
index d101d6376..9cd6d19a8 100644
--- a/apps/docs/docs/widgets/smart-home-entity-state/index.mdx
+++ b/apps/docs/docs/widgets/smart-home-entity-state/index.mdx
@@ -1,34 +1,32 @@
---
-title: 'Entity State'
-description: 'Display the state of an entity and toggle it optionally'
+title: "Entity State"
+description: "Display the state of an entity and toggle it optionally"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { smartHomeEntityStateWidget } from '.';
-import { homeAssistantIntegration } from '@site/docs/integrations/home-assistant';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { smartHomeEntityStateWidget } from ".";
+import { homeAssistantIntegration } from "@site/docs/integrations/home-assistant";
-
-
+
The entity state enables you to display the current state of an entity.
-This works with most entities and enables you display almost any data.
+This works with most entities and enables you display almost any data.
It is recommended that you transform data on the integration as much as possible since Homarr's capabilities in transformation are very limited.
### Screenshots
-import sun from './img/sun.png';
-import lightSwitch from './img/light-switch.png';
-import temperature from './img/temperature.png';
+import sun from "./img/sun.png";
+import lightSwitch from "./img/light-switch.png";
+import temperature from "./img/temperature.png";
@@ -36,32 +34,41 @@ import temperature from './img/temperature.png';
### Supported Integrations
-
+
### Interactions
- Toggle entity state by clicking on the entity (only when clickable enabled).
+- If Homarr cannot refresh an entity, the last known state stays visible but toggling is disabled until the connection recovers.
+
+
### Adding the widget
### Configuration
+
### How to find the entity ID
To find the entity ID in Home Assistant, follow these steps:
+
1. Open Home Assistant and go to the **Developer Tools** section (usually found in the sidebar).
2. Click on the **States** tab.
3. In the list of entities, find the entity you want to use.
4. The entity ID will be displayed in the format `domain.object_id` (e.g., `light.living_room_light`).
5. Copy the entity ID and paste it into the widget configuration in Homarr. It will then show the State that is visible in Home Assistant.
-import retrieveEntityId from './img/retrieve-entity-id.png';
+import retrieveEntityId from "./img/retrieve-entity-id.png";
-
\ No newline at end of file
+
diff --git a/apps/docs/docs/widgets/smart-home-execute-automation/index.mdx b/apps/docs/docs/widgets/smart-home-execute-automation/index.mdx
index 522c113ca..f0118799a 100644
--- a/apps/docs/docs/widgets/smart-home-execute-automation/index.mdx
+++ b/apps/docs/docs/widgets/smart-home-execute-automation/index.mdx
@@ -1,50 +1,60 @@
---
-title: 'Execute Automation'
-description: 'Trigger an automation with one click'
+title: "Execute Automation"
+description: "Trigger an automation with one click"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
import { IconAd } from "@tabler/icons-react";
-import Tabs from '@theme/Tabs';
-import TabItem from '@theme/TabItem';
-import { smartHomeExecuteAutomationWidget } from '.';
-import { homeAssistantIntegration } from '@site/docs/integrations/home-assistant';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import { smartHomeExecuteAutomationWidget } from ".";
+import { homeAssistantIntegration } from "@site/docs/integrations/home-assistant";
-
-
+
The execute automation widget allows you to trigger an automation with one click.
### Screenshots
-import empty from './img/empty.png';
-import withDisplayname from './img/with-displayname.png';
+import empty from "./img/empty.png";
+import withDisplayname from "./img/with-displayname.png";
-
+
### Supported Integrations
-
+
### Interactions
- Trigger the automation by clicking on the widget.
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/speedtest-tracker/index.mdx b/apps/docs/docs/widgets/speedtest-tracker/index.mdx
index 94be18dd7..c6c0bd992 100644
--- a/apps/docs/docs/widgets/speedtest-tracker/index.mdx
+++ b/apps/docs/docs/widgets/speedtest-tracker/index.mdx
@@ -1,39 +1,45 @@
---
-title: 'Speedtest Tracker'
-description: 'Displays speed test results from your Speedtest Tracker instance.'
+title: "Speedtest Tracker"
+description: "Displays speed test results from your Speedtest Tracker instance."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { speedtestTrackerWidget } from '.';
-import { speedtestTrackerIntegration } from '@site/docs/integrations/speedtest-tracker';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { speedtestTrackerWidget } from ".";
+import { speedtestTrackerIntegration } from "@site/docs/integrations/speedtest-tracker";
+
+
This widget displays internet speed test results from your [Speedtest Tracker](https://docs.speedtest-tracker.dev/) instance,
including the latest result, historical averages, and a live chart of the last 12 hours.
### Screenshots
-import screenshot from './img/screenshot.png';
+import screenshot from "./img/screenshot.png";
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/stock-price/index.mdx b/apps/docs/docs/widgets/stock-price/index.mdx
index 5d9a24c64..a95ed13bf 100644
--- a/apps/docs/docs/widgets/stock-price/index.mdx
+++ b/apps/docs/docs/widgets/stock-price/index.mdx
@@ -1,28 +1,27 @@
---
-title: 'Stock Price'
-description: 'Displays the current stock price of a company.'
+title: "Stock Price"
+description: "Displays the current stock price of a company."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { stockPriceWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { stockPriceWidget } from ".";
-
+
Using the Stocks widget, you can show the current stock prices of your favorite companies on your dashboard.
+The widget prioritizes the symbol, current price, and trend. Company name, change, time range, and a taller chart appear progressively when both widget dimensions can support them.
### Screenshots
-import apple from './img/apple.png';
-import google from './img/google.png';
-import ethereum from './img/ethereum.png';
-import nvidia from './img/nvidia.png';
+import apple from "./img/apple.png";
+import google from "./img/google.png";
+import ethereum from "./img/ethereum.png";
+import nvidia from "./img/nvidia.png";
@@ -34,17 +33,21 @@ import nvidia from './img/nvidia.png';
-
+
+
### Adding the widget
### Configuration
+
### Examples
+
Here's a small collection of examples, that you can use for testing or actual use (in no particular order):
+
- AAPL
- GOOGL
- AMZN
@@ -59,4 +62,4 @@ Here's a small collection of examples, that you can use for testing or actual us
### Data source
We use the Yahoo Finance API to fetch the stock prices.
-If you need to whitelist the api, please use the following URL: `https://query1.finance.yahoo.com`
\ No newline at end of file
+If you need to whitelist the api, please use the following URL: `https://query1.finance.yahoo.com`
diff --git a/apps/docs/docs/widgets/system-disks/index.mdx b/apps/docs/docs/widgets/system-disks/index.mdx
index b3be87e1d..d533fa025 100644
--- a/apps/docs/docs/widgets/system-disks/index.mdx
+++ b/apps/docs/docs/widgets/system-disks/index.mdx
@@ -4,6 +4,8 @@ description: "Disk usage of your system"
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -42,6 +44,8 @@ This widget displays filesystem and SMART disk usage for supported server and NA
]}
/>
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/system-disks/index.ts b/apps/docs/docs/widgets/system-disks/index.ts
index 8dd68f057..ade53efc0 100644
--- a/apps/docs/docs/widgets/system-disks/index.ts
+++ b/apps/docs/docs/widgets/system-disks/index.ts
@@ -19,7 +19,11 @@ export const systemDisksWidget: WidgetDefinition = {
description: "How disk usage values are shown",
values: {
type: "select",
- options: ["Percentage (e.g., 76%)", "Absolute values (e.g., 800GB / 1TB)", "Free space percentage (e.g., 24% free)"],
+ options: [
+ "Percentage (e.g., 76%)",
+ "Absolute values (e.g., 800GB / 1TB)",
+ "Free space percentage (e.g., 24% free)",
+ ],
},
defaultValue: "Percentage (e.g., 76%)",
},
diff --git a/apps/docs/docs/widgets/system-resources/index.mdx b/apps/docs/docs/widgets/system-resources/index.mdx
index 16ad19750..cdc258f4c 100644
--- a/apps/docs/docs/widgets/system-resources/index.mdx
+++ b/apps/docs/docs/widgets/system-resources/index.mdx
@@ -4,6 +4,8 @@ description: "Displays CPU, RAM and network of your host in real time"
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -65,6 +67,8 @@ import { synologyIntegration } from "@site/docs/integrations/synology";
]}
/>
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/timetable/index.mdx b/apps/docs/docs/widgets/timetable/index.mdx
index e9bdfd326..1aede99cd 100644
--- a/apps/docs/docs/widgets/timetable/index.mdx
+++ b/apps/docs/docs/widgets/timetable/index.mdx
@@ -4,6 +4,8 @@ description: "Displays departure times for a station."
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
import { WidgetHeader } from "@site/src/components/widgets/header";
@@ -19,18 +21,12 @@ import trainDepartures from "./img/train-departures.png";
import streetDepartures from "./img/street-departures.png";
-
-
+
+
+
+
### Adding the widget
@@ -38,3 +34,7 @@ import streetDepartures from "./img/street-departures.png";
### Configuration
+
+For security, all custom base URLs must resolve to a public IP address whenever Homarr uses them. Private and local hosts are rejected.
+
+If a timetable refresh fails after departures have loaded, the widget keeps the previous departures visible and marks them as stale. The advanced view also shows their last update time.
diff --git a/apps/docs/docs/widgets/timetable/index.ts b/apps/docs/docs/widgets/timetable/index.ts
index 1cefb6bcd..32b9e2bcf 100644
--- a/apps/docs/docs/widgets/timetable/index.ts
+++ b/apps/docs/docs/widgets/timetable/index.ts
@@ -11,7 +11,7 @@ export const timetableWidget: WidgetDefinition = {
{
name: "Search.ch URL",
description: "Base URL for the Search.ch timetable API.",
- values: "Any valid URL.",
+ values: "An HTTP(S) URL that resolves to a public IP address.",
defaultValue: "https://search.ch",
},
{
diff --git a/apps/docs/docs/widgets/tracearr/index.mdx b/apps/docs/docs/widgets/tracearr/index.mdx
index a579d8af3..ff02699ca 100644
--- a/apps/docs/docs/widgets/tracearr/index.mdx
+++ b/apps/docs/docs/widgets/tracearr/index.mdx
@@ -1,52 +1,57 @@
---
-title: 'Tracearr'
-description: 'Monitor media server streams, user activity, and policy violations.'
+title: "Tracearr"
+description: "Monitor media server streams, user activity, and policy violations."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { tracearrWidget } from '.';
-import { tracearrIntegration } from '@site/docs/integrations/tracearr';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { tracearrWidget } from ".";
+import { tracearrIntegration } from "@site/docs/integrations/tracearr";
-
+
The Tracearr widget provides a real-time monitoring dashboard for your media server activity. It displays active streams, user statistics, recent watching history, and policy violations.
### Sections
-| Section | Description |
-|---|---|
-| **Stats Bar** | Displays Active Streams, Total Users, Transcode Count, and Total Bandwidth at a glance. |
-| **Active Streams** | Shows currently playing media with poster art, user info, progress bar, resolution, and transcoding status (Direct Play or Transcode). |
-| **Recent Activity** | A paginated list of watching history with user avatars and watched status. |
-| **Violations** | Alerts for policy violations (e.g., concurrent stream limits) with severity levels. |
+| Section | Description |
+| ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
+| **Stats Bar** | Displays Active Streams, Total Users, Transcode Count, and Total Bandwidth at a glance. |
+| **Active Streams** | Shows currently playing media with poster art, user info, progress bar, resolution, and transcoding status (Direct Play or Transcode). |
+| **Recent Activity** | A paginated list of watching history with user avatars and watched status. |
+| **Violations** | Alerts for policy violations (e.g., concurrent stream limits) with severity levels. |
### Screenshots
-import widgetOverview from './img/widget-overview.png';
-import streamsAndStats from './img/streams-and-stats.png';
+import widgetOverview from "./img/widget-overview.png";
+import streamsAndStats from "./img/streams-and-stats.png";
+
+
### Adding the widget
### Supported Integrations
-
+
### Configuration
+
diff --git a/apps/docs/docs/widgets/traefik/index.mdx b/apps/docs/docs/widgets/traefik/index.mdx
index dc9f6a6c4..dfc2f9247 100644
--- a/apps/docs/docs/widgets/traefik/index.mdx
+++ b/apps/docs/docs/widgets/traefik/index.mdx
@@ -4,6 +4,8 @@ description: "Displays Traefik router, service, middleware, and entry point coun
hide_title: true
---
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+
import { WidgetHeader } from "@site/src/components/widgets/header";
import { AddingWidget } from "@site/src/components/widgets/adding";
import { WidgetConfig } from "@site/src/components/widgets/configuration";
@@ -26,6 +28,8 @@ This widget displays a compact overview of your Traefik instance, including tota
]}
/>
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/umami/index.mdx b/apps/docs/docs/widgets/umami/index.mdx
index 0d0006054..6f2c2bb7f 100644
--- a/apps/docs/docs/widgets/umami/index.mdx
+++ b/apps/docs/docs/widgets/umami/index.mdx
@@ -1,28 +1,33 @@
---
-title: 'Umami Analytics'
-description: 'Display visitor stats from your Umami analytics instance'
+title: "Umami Analytics"
+description: "Display visitor stats from your Umami analytics instance"
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { umamiWidget } from '.';
-import { umamiIntegration } from '@site/docs/integrations/umami';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { umamiWidget } from ".";
+import { umamiIntegration } from "@site/docs/integrations/umami";
+
+
The Umami Analytics widget displays key visitor metrics from a website tracked by your [Umami](https://umami.is) instance - pageviews, unique visitors, visits, bounce rate, avg. visit duration, and real-time active visitor count. The widget body can be switched between a visitor/event chart, a multi-event chart, and ranked top pages or referrers lists.
### Supported Integrations
-
+
+
+
### Adding the widget
@@ -34,12 +39,12 @@ The Umami Analytics widget displays key visitor metrics from a website tracked b
### View modes
-| View | Description |
-|---|---|
-| **Chart** | Bar chart or sparkline of unique visitors over the selected time frame. Optionally overlay a single named event as a second series. |
-| **Events chart** | Multi-select one or more named events and chart each as its own colored series - no visitor baseline. Supports bar and sparkline. |
-| **Top pages** | Ranked list of URLs by pageview count for the selected time frame. |
-| **Top referrers** | Ranked list of referrer sources by session count. Direct traffic (no referrer) is labeled "Direct". |
+| View | Description |
+| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
+| **Chart** | Bar chart or sparkline of unique visitors over the selected time frame. Optionally overlay a single named event as a second series. |
+| **Events chart** | Multi-select one or more named events and chart each as its own colored series - no visitor baseline. Supports bar and sparkline. |
+| **Top pages** | Ranked list of URLs by pageview count for the selected time frame. |
+| **Top referrers** | Ranked list of referrer sources by session count. Direct traffic (no referrer) is labeled "Direct". |
### Notes
diff --git a/apps/docs/docs/widgets/ups/index.mdx b/apps/docs/docs/widgets/ups/index.mdx
index bf512990d..8a3d7037e 100644
--- a/apps/docs/docs/widgets/ups/index.mdx
+++ b/apps/docs/docs/widgets/ups/index.mdx
@@ -1,20 +1,19 @@
---
-title: 'UPS'
-description: 'Monitor the status of your UPS devices through a NUT server.'
+title: "UPS"
+description: "Monitor the status of your UPS devices through a NUT server."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { upsWidget } from '.';
-import { peaNutIntegration } from '@site/docs/integrations/peanut';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { upsWidget } from ".";
+import { peaNutIntegration } from "@site/docs/integrations/peanut";
+
+
This widget displays the status of the UPS devices reported by a Network UPS Tools (NUT) server. Each device shows its
status (online, on battery, charging or low battery), battery charge and estimated runtime, output load, and input and
@@ -22,27 +21,34 @@ output voltage.
### Layouts
-import screenshotFull from './img/screenshot.png';
-import screenshotCompact from './img/screenshot-compact.png';
-import screenshotMini from './img/screenshot-mini.png';
+import screenshotFull from "./img/screenshot.png";
+import screenshotCompact from "./img/screenshot-compact.png";
+import screenshotMini from "./img/screenshot-mini.png";
The widget adapts to the size of the tile, so it stays readable from a full dashboard panel down to a single cell. Multiple UPS devices scroll within the widget at any height.
-| Layout | Size | Preview |
-| --- | --- | --- |
-| Full | 3+ columns wide | |
-| Compact | ~2 columns | |
-| Mini | 1 column | |
+| Layout | Size | Preview |
+| ------- | --------------- | --------------------------------------------------------------------------- |
+| Full | 3+ columns wide | |
+| Compact | ~2 columns | |
+| Mini | 1 column | |
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/uptime-kuma/index.mdx b/apps/docs/docs/widgets/uptime-kuma/index.mdx
index 3a6592b2e..6c5eefa16 100644
--- a/apps/docs/docs/widgets/uptime-kuma/index.mdx
+++ b/apps/docs/docs/widgets/uptime-kuma/index.mdx
@@ -1,34 +1,39 @@
---
-title: 'Uptime Kuma'
-description: 'Displays monitor uptime statistics, average uptime percentage, and service status counts.'
+title: "Uptime Kuma"
+description: "Displays monitor uptime statistics, average uptime percentage, and service status counts."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { uptimeKumaWidget } from '.';
-import { uptimeKumaIntegration } from '@site/docs/integrations/uptime-kuma';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { uptimeKumaWidget } from ".";
+import { uptimeKumaIntegration } from "@site/docs/integrations/uptime-kuma";
-
+
This widget displays uptime statistics from your Uptime Kuma status page, including a visual uptime ring, average uptime percentage, and per-status monitor counts.
### Supported Integrations
-
+
+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/video/index.mdx b/apps/docs/docs/widgets/video/index.mdx
index 3ccf3760a..fbf41c4e3 100644
--- a/apps/docs/docs/widgets/video/index.mdx
+++ b/apps/docs/docs/widgets/video/index.mdx
@@ -1,19 +1,17 @@
---
-title: 'Video Stream'
-description: 'Embed a video stream or video from a camera or a website.'
+title: "Video Stream"
+description: "Embed a video stream or video from a camera or a website."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { videoWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { videoWidget } from ".";
-
+
Do you run surveilance cameras? Or maybe you have a favourite video?
The video widget enables you to integrate both seamlessly into your dashboard.
@@ -22,7 +20,6 @@ This widget uses [video.js](https://videojs.com/) to play HLS or DASH video nati
DRM content is not yet supported. More info here:
https://videojs.com/guides/faqs/#q-what-is-videojs
-
You can use the following URL to test:
http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4
@@ -30,9 +27,12 @@ http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp

+
+
### Adding the widget
### Configuration
+
diff --git a/apps/docs/docs/widgets/video/index.tsx b/apps/docs/docs/widgets/video/index.tsx
index e0df87ce0..7d57a27ad 100644
--- a/apps/docs/docs/widgets/video/index.tsx
+++ b/apps/docs/docs/widgets/video/index.tsx
@@ -5,7 +5,7 @@ export const videoWidget: WidgetDefinition = {
icon: IconDeviceCctv,
name: "Video Stream",
description: "Embed a video stream or video from a camera or a website.",
- path: "../../widgets/stock-price",
+ path: "../../widgets/video",
configuration: {
items: [
{
diff --git a/apps/docs/docs/widgets/vpn/index.mdx b/apps/docs/docs/widgets/vpn/index.mdx
index 0aa6a1a16..0e23619e5 100644
--- a/apps/docs/docs/widgets/vpn/index.mdx
+++ b/apps/docs/docs/widgets/vpn/index.mdx
@@ -1,19 +1,18 @@
---
-title: 'VPN'
-description: 'Monitor the status of your VPN integrations.'
+title: "VPN"
+description: "Monitor the status of your VPN integrations."
hide_title: true
---
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetIntegrations } from '@site/src/components/widgets/integrations';
-import { vpnWidget } from '.';
-import { gluetunIntegration } from '@site/docs/integrations/gluetun';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
-
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetIntegrations } from "@site/src/components/widgets/integrations";
+import { vpnWidget } from ".";
+import { gluetunIntegration } from "@site/docs/integrations/gluetun";
+
+
The VPN widget provides a compact overview of each configured VPN integration. It shows whether the tunnel and DNS
services are up, the currently exposed public IP address, and the VPN provider and protocol being used.
@@ -23,7 +22,7 @@ current status at a glance.
### Screenshots
-import singleIntegrationImage from './img/single.png';
+import singleIntegrationImage from "./img/single.png";
@@ -31,9 +30,15 @@ import singleIntegrationImage from './img/single.png';
### Supported Integrations
-
+
+
+
### Adding the widget
diff --git a/apps/docs/docs/widgets/weather/index.mdx b/apps/docs/docs/widgets/weather/index.mdx
index 0ff4081d7..f36dc68ea 100644
--- a/apps/docs/docs/widgets/weather/index.mdx
+++ b/apps/docs/docs/widgets/weather/index.mdx
@@ -1,40 +1,44 @@
---
-title: 'Weather'
-description: 'Displays the current weather information of a set location.'
+title: "Weather"
+description: "Displays the current weather information of a set location."
hide_title: true
---
-import { AddingWidget } from '@site/src/components/widgets/adding';
-import { WidgetConfig } from '@site/src/components/widgets/configuration';
-import { WidgetHeader } from '@site/src/components/widgets/header';
-import { weatherWidget } from '.';
+import { WidgetInteractionGuide } from "@site/src/components/widgets/interactions";
+import { AddingWidget } from "@site/src/components/widgets/adding";
+import { WidgetConfig } from "@site/src/components/widgets/configuration";
+import { WidgetHeader } from "@site/src/components/widgets/header";
+import { weatherWidget } from ".";
-
+
The Weather Widget uses your location to display the current, highest and lowest temperature.
+In the compact view, select a condition icon or forecast day to open details with a mouse, keyboard or touch.
+The advanced view always combines current conditions with the full seven-day forecast, including humidity, wind,
+gusts, sunrise and sunset. Both views reuse the same cached Open Meteo response.
### Screenshots
-import forecastWeather from './img/forecast.png';
-import currentWeather from './img/current.png';
+import forecastWeather from "./img/forecast.png";
+import currentWeather from "./img/current.png";
+ Every available component also receives these {catalog.globalProps.length} generated global, ARIA, data, and
+ safe style props. Component cards below list only their component-specific additions.
+