Skip to main content

CnWidgetWrapper

Container shell around a dashboard widget. Provides a header with icon and title, a scrollable content area, and an optional footer with action links. Accepts a styleConfig object for runtime style overrides (background, border, padding). Used internally by CnDashboardPage for all non-tile widgets.

Try it

Loading CnWidgetWrapper playground…

Usage

<!-- Basic wrapper -->
<CnWidgetWrapper title="My Cases" icon-url="/apps/myapp/img/icon.svg">
<MyCasesChart :data="chartData" />
</CnWidgetWrapper>

<!-- Without header (borderless, flush — for self-contained card widgets) -->
<CnWidgetWrapper :show-title="false" :borderless="true">
<CnStatsBlock :stats="kpis" />
</CnWidgetWrapper>

<!-- With NC widget object (used by CnDashboardPage internally) -->
<CnWidgetWrapper
:title="widget.title"
:icon-url="widget.iconUrl"
:icon-class="widget.iconClass"
:buttons="widget.buttons">
<CnWidgetRenderer :widget="widget" />
</CnWidgetWrapper>

<!-- With custom footer and header actions -->
<CnWidgetWrapper title="Tasks">
<template #actions>
<NcButton type="tertiary" @click="refresh">Refresh</NcButton>
</template>
<TaskList :items="tasks" />
<template #footer>
<a href="/apps/tasks">View all</a>
</template>
</CnWidgetWrapper>

Props

PropTypeDefaultDescription
titleString'Widget'Widget title shown in the header
showTitleBooleantrueWhether to render the header bar
chromeString'default'Card chrome variant. 'default' uses the library card chrome; 'nc-dashboard' reproduces the native Nextcloud Dashboard panel exactly (translucent blurred background via --color-main-background-blur + --filter-background-blur, --border-radius-container-large corners, no border/shadow, 16px header with a 20px/700 title and 32px leading icon, content inset 16px). styleConfig overrides still layer on top.
showActionsBooleantrueWhether the header's overflow action menu (Refresh / Documentation / Request-a-feature + #action-items) renders. Set false for compact surfaces (e.g. a KPI tile) to drop the menu and free header width.
borderlessBooleanfalseRemove border and background — makes the wrapper transparent
flushBooleanfalseRemove content padding — lets content extend edge-to-edge
iconUrlStringnullImage URL for the header icon
iconClassStringnullCSS class for the header icon (e.g. Nextcloud icon class)
titleIconPositionString'right'Position of the title-icon slot in the header: 'left' places it before the title group; 'right' places it after the actions
titleIconColorStringnullCSS color value applied to the title-icon slot container (e.g. '#e74c3c')
buttonsArray[]Footer button links: [{ text, link }]
styleConfigObject{}Runtime style overrides: { backgroundColor?, borderStyle?, borderWidth?, borderColor?, borderRadius?, padding?: { top, right, bottom, left }, headerStyle?: { backgroundColor?, textColor? } }. The optional headerStyle colours the header bar per-widget.
refreshingBooleanfalseWhen bound (e.g. :refreshing="loading" around the host's refetch), the Refresh item is disabled and shows a loading spinner for exactly as long as this stays true — so the spinner reflects the real refresh time.

Slots

SlotDescription
defaultWidget content rendered in the scrollable body area
actionsButtons or controls placed in the right side of the header
title-iconExtra icon element rendered in the header at the position controlled by titleIconPosition (left of title or right of actions)
footerCustom footer content (replaces the buttons prop rendering)

Built-in Actions menu

CnWidgetWrapper ships with a small overflow menu in the header — the shared CnActionsMenu — containing up to three actions, all functional without any host wiring when the wrapper is mounted under CnAppRoot:

  • Refresh — emits @refresh, then (unless the host listener calls event.preventDefault()) emits on the @nextcloud/event-bus channel cn:widget:refresh with payload { widgetId, title }. Widgets that care subscribe and filter by widgetId.
  • Documentation — rendered only when a documentationUrl is supplied. Opens the host-provided link in a new tab (target="_blank", rel="noopener noreferrer"); no JS handler. Apps pass the URL from the widget configuration (:documentation-url="widget.documentationUrl"); customise the wording with documentationLabel.
  • Request a feature — emits @request-feature, then (unless suppressed) auto-mounts CnSuggestFeatureModal with app + page + surface=widget:<id> context auto-filled from CnAppRoot injects. The host can override the default by binding @request-feature and calling event.preventDefault() to handle it themselves.

Opt out per-instance with :show-refresh="false" and/or :show-request-feature="false" (the legacy hide-refresh / hide-request-feature aliases also still work for back-compat). When everything is hidden — both built-ins opted out, no documentationUrl, and no #action-items slot content — the overflow menu disappears entirely. To drop the entire actions area in one go — e.g. on a compact KPI tile whose only header affordance is a date chip — set :show-actions="false".

Set :widget-id so the event-bus payload + modal surface tag are stable across renames; otherwise the wrapper falls back to a slugified title.

Opting into Refresh

A widget can opt in to Refresh in one of three ways. Pick whichever fits the widget's existing reactivity model — all three are first-class.

1. Ref-callable refresh() method (canonical)

The cleanest pattern: expose a method, let the host call it through the ref.

<template>
<CnWidgetWrapper title="Outgoing calls" widget-id="outgoing-calls-daily">
<CallsChart ref="chart" />
</CnWidgetWrapper>
</template>

<script>
export default {
// Inside CallsChart.vue:
methods: {
async refresh() {
this.data = await this.$store.dispatch('callLogs/refetch')
},
},
}
</script>

The host listens via the event-bus channel and routes by id (see mode 3 below for the wiring), or the wrapper invokes the method directly when integrated.

2. Reactive refreshTrigger prop

Useful when the widget cannot easily expose a ref (e.g. async-loaded). The host increments a timestamp; the widget watches it.

<template>
<CnWidgetWrapper title="Outgoing calls" widget-id="outgoing-calls-daily">
<CallsChart :refresh-trigger="callsRefreshTrigger" />
</CnWidgetWrapper>
</template>

<script>
import { subscribe } from '@nextcloud/event-bus'
export default {
data() {
return { callsRefreshTrigger: 0 }
},
mounted() {
subscribe('cn:widget:refresh', ({ widgetId }) => {
if (widgetId === 'outgoing-calls-daily') {
this.callsRefreshTrigger = Date.now()
}
})
},
}
</script>

3. Event-bus subscription (direct)

The widget subscribes itself; no host plumbing needed.

<script>
import { subscribe, unsubscribe } from '@nextcloud/event-bus'
export default {
props: { widgetId: { type: String, required: true } },
mounted() {
this._onRefresh = ({ widgetId }) => {
if (widgetId === this.widgetId) this.refetch()
}
subscribe('cn:widget:refresh', this._onRefresh)
},
beforeDestroy() {
unsubscribe('cn:widget:refresh', this._onRefresh)
},
methods: {
async refetch() { /* ... */ },
},
}
</script>

Reference (auto-generated)

The tables below are generated from the SFC source via vue-docgen-cli. They reflect what's actually in CnWidgetWrapper.vue and update automatically whenever the component changes.

Props

NameTypeRequiredDefaultDescription
titlestring() =&gt; t('nextcloud-vue', 'Widget')Widget title
showTitlebooleantrueWhether to show the header with title
chromestring'default'Chrome variant for the wrapper card. - 'default' — the library's own card chrome (opaque background, 1px border, compact header). - 'nc-dashboard' — matches the native Nextcloud Dashboard panel exactly using the same design tokens: translucent blurred background (--color-main-background-blur + --filter-background-blur), --border-radius-container-large corners, no border/shadow, a 16px header with a 20px/700 title and 32px leading icon, and content inset 16px on the sides + bottom. styleConfig overrides still layer on top so a user can customise any token.
borderlessbooleanfalseRemove border and background — makes the wrapper transparent. Useful for widgets that are self-contained cards (e.g. CnStatsBlock).
flushbooleanfalseRemove content padding — allows content to go edge-to-edge. Useful for list-style widgets where items should span the full width.
iconUrlstringnullIcon URL (image)
iconClassstringnullIcon CSS class (e.g., Nextcloud icon class)
titleIconPositionstring'right'Position of the title-icon slot in the header. 'left' places it before the title; 'right' places it after the actions.
titleIconColorstringnullCSS color value applied to the title-icon slot container
buttonsarray[]Footer action buttons: [{ text, link }]
showActionsbooleantrueWhether the header's overflow action menu (Refresh / Documentation / Request-a-feature + any #action-items) renders. Shown by default; set false for compact surfaces — e.g. a KPI tile whose only header affordance is a date chip — to drop the menu and free header width.
styleConfig{ backgroundColor: string, borderStyle: string, borderWidth: number, borderColor: string, borderRadius: number, padding: { top: number, right: number, bottom: number, left: number } }\{\}Style configuration for the wrapper.
hideRefreshbooleanfalseHide the built-in Refresh item from the overflow action menu. The Refresh item is shown by default — set this when the widget has no refreshable data source (e.g. a static tile). Alias for the inverse :show-refresh="false"; either form opts out.
hideRequestFeaturebooleanfalseHide the built-in Request-a-feature item from the overflow action menu. Shown by default; set when the consuming app has no public issue tracker to link out to. Alias for the inverse :show-request-feature="false"; either form opts out.
showRefreshbooleantrueInverse of hideRefresh. Defaults to true so the action renders. Set :show-refresh="false" to hide. Either hideRefresh OR !showRefresh hides the action — the spec scenario in widget-wrapper declares the show-prefixed form as canonical; the hide- flags remain for back-compat.
showRequestFeaturebooleantrueInverse of hideRequestFeature. Defaults to true so the action renders. Set :show-request-feature="false" to hide it. Either flag hides the action.
documentationUrlstring''Documentation link for this widget. When a non-empty URL is set, the overflow menu renders a "Documentation" item that opens the link in a new tab. Host apps supply it from the widget configuration. Empty (the default) hides the item.
documentationLabelstring() =&gt; t('nextcloud-vue', 'Documentation')Optional pre-translated label for the Documentation action. Defaults to the lib's translation of "Documentation".
widgetIdstring''Widget id for the built-in default Refresh / Request-a-feature handlers (B2). Forwarded as the surface: "widget:&lt;id&gt;" value on the auto-mounted CnSuggestFeatureModal AND as the widgetId field on the cn:widget:refresh event-bus payload. When unset, the wrapper falls back to a slugified displayTitle, which works but is less stable than an explicit id.
specRefstring''Optional specRef forwarded to the auto-mounted CnSuggestFeatureModal so the resulting GitHub issue links to the spec capability this widget belongs to.
refreshingbooleanfalseWhether a refresh is currently in flight. When bound by the host (e.g. :refreshing="loading" around its refetch), the Refresh item is disabled and shows a loading spinner for exactly as long as this stays true — so the spinner reflects the real refresh time.
refreshLabelstring() =&gt; t('nextcloud-vue', 'Refresh')Optional pre-translated label for the Refresh action. Defaults to the lib's translation of "Refresh" so callers usually don't need to set this.
requestFeatureLabelstring() =&gt; t('nextcloud-vue', 'Request a feature')Optional pre-translated label for the Request-a-feature action. Defaults to the lib's translation of "Request a feature".
actionsMenuLabelstring() =&gt; t('nextcloud-vue', 'Actions')Pre-translated aria-label / tooltip for the overflow menu trigger. Defaults to "Actions".

Events

NamePayloadDescription
refreshundefinedUser clicked the Refresh item in the overflow action menu. Payload: { widgetId, title }. Handlers may call the second arg's preventDefault() to suppress the built-in default (event-bus emit on cn:widget:refresh).
request-featureundefinedUser clicked the Request a feature item. Payload: { widgetId, title }. Handlers may call the second arg's preventDefault() to suppress the built-in default (auto-opening CnSuggestFeatureModal).

Slots

NameBindingsDescription
title-icon
title-meta
actionsactions Custom action buttons rendered before the
action-items
default
footer

Additional props & slots

PropTypeDefaultDescription
specRefString''Forwarded to the auto-mounted CnSuggestFeatureModal so the resulting issue links to the widget's spec capability.
documentationUrlString''When set, renders a Documentation item in the overflow menu that opens this link in a new tab. Empty hides it.
documentationLabelStringt('Documentation')Pre-translated label for the Documentation action.
refreshLabelStringt('Refresh')Pre-translated label for the Refresh action.
requestFeatureLabelStringt('Request a feature')Pre-translated label for the Request-a-feature action.
actionsMenuLabelStringt('Actions')Pre-translated aria-label / tooltip for the overflow menu trigger.
SlotDescription
title-metaInline content rendered next to the title (e.g. the dashboard date-range chip).