Skip to content

Plugin API for custom sidebar panels #5971

Description

@z80dev

Summary

Provide an API that allows plugins to register custom sections/panels in the sidebar.

Motivation

Currently, plugins can extend OpenCode with tools, hooks, agents, and MCP configs - but have no way to surface custom UI in the sidebar. The sidebar is entirely controlled by OpenCode internals (MCPs, context usage, todos, etc.).

For plugins like oh-my-opencode, there are several things we'd want to display:

  • Background agent task status (running/completed/failed)
  • Custom metrics or state tracked by hooks
  • Plugin-specific configuration status
  • Agent availability/health indicators

Implemented API

// In @opencode-ai/plugin

interface SidebarPanelItem {
  label: string
  value?: string
  status?: "success" | "warning" | "error" | "info"
}

interface SidebarPanel {
  id: string
  title: string
  items: SidebarPanelItem[] | (() => SidebarPanelItem[])
}

interface Hooks {
  // ... existing hooks
  sidebar?: SidebarPanel[] | (() => SidebarPanel[])
}

Usage Example

import { definePlugin } from "@opencode-ai/plugin"

export default definePlugin({
  name: "my-plugin",
  hooks: {
    sidebar: [
      {
        id: "status",
        title: "My Plugin Status",
        items: [
          { label: "Version", value: "1.0.0" },
          { label: "Status", value: "Active", status: "success" }
        ]
      }
    ]
  }
})

Dynamic Updates

Both sidebar and items can be functions for dynamic content. The sidebar polls every 5 seconds for updates:

hooks: {
  sidebar: () => [{
    id: "metrics",
    title: "Live Metrics",
    items: () => [
      { label: "Requests", value: String(requestCount) },
      { label: "Last Update", value: new Date().toLocaleTimeString() }
    ]
  }]
}

Implementation Details

  • Server endpoint: GET /plugin/sidebar returns aggregated panels from all plugins
  • UI: Plugin panels render in the sidebar with collapse/expand support
  • Polling: 5-second refresh interval with proper cleanup
  • Error isolation: Individual plugin failures are logged but don't affect others

Activity

  1. z80dev commented on Dec 29, 2025

    @z80dev
    Author

    First-pass implementation available in #6389

  2. fatihdogmus commented on Jan 12, 2026

    @fatihdogmus

    that would be pretty nice since I wanted to add a status section where I can see my codex or claude code quota but wasn't able to. Claude code has somewhat customizable status bar, so this issue would be pretty neat.

  3. mathisdev7 commented on Jan 12, 2026

    @mathisdev7

    this is a really solid addition. persistent sidebar panels are exactly whats been missing for things like quota visibility

    i ran into this problem deeply while working on arctic (a focused fork of opencode for plan-based usage visibility). one thing that became clear is that quota tracking only stays accurate when the client owns the request flow

    sidebar hooks make a clean UX possible, but if plugins have to infer usage or rely on extra agent/tool calls, things get noisy or inaccurate fast

    still, this API unlocks a lot of previously impossible UI

  4. art-shen commented on Jan 13, 2026

    @art-shen

    Yes, please enable OpenCode plugins to display additional helpful information in the sidebar or elsewhere!

    Without this functionality, some plugins that could display available quota for requests or other important status information can’t be used efficiently.

    Example of a feature request in a plugin that was closed as impossible at the moment:
    NoeFabris/opencode-antigravity-auth#152

  5. js-krinay commented on Feb 24, 2026

    @js-krinay

    Improvements

    1. No SSE/reactive updates. The client polls a REST endpoint every 5s. This means the Web client would need to independently implement the same polling loop. Using the existing SSE event bus, both TUI and Web would get updates automatically through sync.data.
    2. No diff detection. Every 5s poll returns the full panel list, even if nothing changed. Server-side diffing would only broadcast changes, reducing SSE traffic.
    3. No user-configurable ordering. Panels appear in whatever order plugins register them. A widget key in opencode.json would let users reorder or hide widgets via config.
    4. No content format flexibility. Items are strictly label: string, value?: string — you can't render a multi-line git diff, a markdown table, or freeform text. Supporting freeform content with format: "text" | "markdown" would cover more use cases.
    5. No widget limits or guardrails. No max panels per plugin, no content size limits, no throttling. A buggy plugin could register unlimited panels.
    6. No ID namespacing. Panel id is a bare string — two plugins could collide on "status".
    7. No cleanup on unmount. The setInterval is cleaned up via onCleanup, but there's no dispose hook for plugin-level teardown.
    8. No overlap guard. The 5s setInterval doesn't check if the previous fetch completed. Slow responses could stack up.
    9. TUI only. No Web implementation.
  6. Goldppx commented on Apr 3, 2026

    @Goldppx

    How's it going now

  7. code-lixm commented on Apr 9, 2026

    @code-lixm

    How's it going now

  8. yulinyuzhu commented on Apr 27, 2026

    @yulinyuzhu

    Looking forward to any updates

  9. eggyShrimp commented on May 19, 2026

    @eggyShrimp

    How's it going now

  10. th3wingman commented on Jun 11, 2026

    @th3wingman

    is this still going in?

  11. spde commented on Jun 26, 2026

    @spde

    Strong +1. We run a set of org-standard opencode plugins, one of which computes a per-session "operational entropy" signal (a session-shape/health score) and surfaces it in the TUI sidebar. For developers on the desktop/web clients that signal silently disappears — there's no renderer extension point, so a whole class of plugin-provided status UX is TUI-only today.

    Two things that would make this land well:

    1. Target the shared renderer, not desktop-only. Since the architecture is one-server/many-clients (TUI, desktop, web, PWA), a declarative status-contribution slot benefits all of them at once.
    2. Drive it off the existing event stream (as @js-krinay noted) rather than a polling endpoint — the server→client SSE subscription already exists as the transport, and a small declarative SidebarPanel payload keeps arbitrary plugin JS out of the renderer, which matters for the web/PWA security model.

    Happy to test against a real third-party plugin if a design lands.

  12. panhaoyu commented on Aug 16, 2026

    @panhaoyu

    Looking forward to the update of this feature.

  13. neriousy commented on Oct 1, 2026

    @neriousy
    Member

    TUI plugins in OpenCode v2 can add their own sidebar sections through the sidebar.content and sidebar.footer slots (context.ui.slot(...)). The built-in Context and MCP panels use the same API.

    Added in 44cd984 and expanded in #41189.

    Closing as completed. If something doesn't work on v2, please open a new issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions