# Custom widgets

Widgets are info panels shown in drawers, modals, or elsewhere in the app (the
config editor, feature detail popups, the add-track form). A widget pairs a
state model with a React component, registered via
`pluginManager.addWidgetType`.

Register a widget in `index.ts`. `HelpWidget` is a whole one — the config
schema, the state model and the registration, in the file that exports the
plugin-install function:

<!-- include: plugins/menus/src/HelpWidget/index.ts -->

```ts
import { ConfigurationSchema } from '@jbrowse/core/configuration'
import { WidgetType } from '@jbrowse/core/pluggableElementTypes'
import { lazyWithPreload } from '@jbrowse/core/util/lazyWithPreload'
import { ElementId } from '@jbrowse/core/util/types/mst'
import { types } from '@jbrowse/mobx-state-tree'

import type PluginManager from '@jbrowse/core/PluginManager'

const configSchema = ConfigurationSchema('HelpWidget', {})

const stateModel = types.model('HelpWidget', {
  id: ElementId,
  type: types.literal('HelpWidget'),
})

export default function HelpWidgetF(pluginManager: PluginManager) {
  pluginManager.addWidgetType(() => {
    return new WidgetType({
      name: 'HelpWidget',
      discardOnClose: true,
      heading: 'Help',
      configSchema,
      stateModel,
      ReactComponent: lazyWithPreload(
        () => import('./components/HelpWidget.tsx'),
      ),
    })
  })
}
```

Every widget state model has `id` and `type`. `ElementId` is
`types.optional(types.identifier, …)`, generating a nanoid on the restore path
when a snapshot arrives without one; `addWidget` always passes the id
explicitly. The `types.literal` tells MST which model to rehydrate a saved
session into. `ReactComponent` is `lazy`-loaded, so a widget's UI code is only
fetched the first time it opens.

`heading` is the static drawer title. Two more `WidgetType` options replace or
extend it:

- **`HeadingComponent`** takes over the title entirely, receiving the widget
  model as `model` — the configuration editor uses one, to name the track being
  edited.
- **`helpText`** puts a help button beside the title that opens what you pass in
  a dialog.

A widget that displays something declares its own fields on the state model
alongside `id` and `type`, and receives them as `addWidget`'s third argument —
`UcscResultsWidget` in the BLAT plugin is that shape, holding the hits its table
renders.

Closing a widget hides it, and it stays in the session with everything it holds.
A widget that shows a result rather than a tool a user returns to — a feature's
details, a search's hits — sets **`discardOnClose: true`** on its `WidgetType`,
so closing removes it and what it holds stops riding along in every session a
user saves or shares.

## Opening a widget

A widget is opened by the `name` its `WidgetType` was registered under. BLAT
opens its results table that way when a search returns hits:

<!-- include: plugins/blat/src/ucscShared.ts#showWidget -->

```ts
// openWidget constructs the widget and puts it in the drawer, which is
// addWidget followed by showWidget. The third argument is the initial state,
// so its keys are the properties UcscResultsWidget's state model declares. Not
// every session has a drawer — an embedded component may be built without one
// — so the guard is not optional.
if (isSessionModelWithWidgets(session)) {
  session.openWidget('UcscResultsWidget', 'ucscResults', {
    features,
    assembly,
    trackName,
    resultNoun,
  })
}
```

**That second argument is the widget's identity, not a label.**
`session.widgets` is a map keyed by it, so `addWidget` with an id already in the
map replaces what was there. BLAT's fixed `'ucscResults'` therefore means every
search reuses one results widget; pass a fresh id (`createElementId()`, from the
same module as `ElementId`) where you want instances to coexist.

See [](https://jbrowse.org/jb2/docs/developer_guides/drawer_widgets) for the rest of the drawer:
position, width, minimizing, and closing a widget again.

## See also

- [](https://jbrowse.org/jb2/docs/developer_guides/drawer_widgets)
- [](https://jbrowse.org/jb2/docs/developer_guides/extension_points)
- [](https://jbrowse.org/jb2/docs/developer_guides/configuration_schema)
- [](https://jbrowse.org/jb2/docs/developer_guides/pluggable_elements)

