How it works

Both components determine the initial state on every page load from these sources, checked in order:

  1. URL param (?panel=): Reading of the URL parameter will always be done, no config needed. See Deep linking.
  2. localStorage: used when persist is enabled and no URL param applied. See below.
  3. HTML default: the markup state like class="active" panel (PanelSet) or class="is-open" (Panel / closable PanelSet).

localStorage is only read when you opt in via persist. A component without it always uses the URL param or its HTML default, so a stored entry from another page can never reopen it unexpectedly.

On this page we delve a bit deeper into persisting state.

Reading localStorage

Reading is opt-in. localStorage is only checked on load when the component has persist enabled. Without it, the stored value is ignored and the HTML default is used.

State is stored under keys scoped to the current page path, so auto-assigned ids (panel-1, panel-2, …) on different pages never collide:

Component Key format Example (on /docs/tabs)
PanelSet <pathname>::ps:<container-id> /docs/tabs::ps:main-tabs
Panel <pathname>::panel:<element-id> /docs/tabs::panel:sidebar

The <pathname> prefix is location.pathname, added automatically. You don’t set it. It just keeps each page’s state separate.

Writing localStorage

Writing is optional. Enable persist to save panel state to localStorage whenever a panel opens or closes. On the next load, the stored state is restored automatically.

Persist in Panel

Key Features

  • This is our most important feature ever!
JavaScript
new Panel('#sidebar', { persist: true });
// or via init
Panel.init({ persist: true });
HTML
<div id="sidebar" data-panel data-panel-persist>...</div>

Persist in PanelSet

Key Features

  • This is our most important feature ever!
JavaScript
new PanelSet('#tabs', { persist: true });
// or via init
PanelSet.init({ persist: true });
HTML
<div id="tabs" data-panelset data-panel-persist>...</div>

Note: a PanelSet uses data-panel-persist (the panel- prefix), not data-panelset-persist. The persistence attributes are shared with Panel, so the prefix stays panel- on both.

Play around with the two examples above, and reload the page. Both come back in the same state you left them.

For accordion groups, set data-panel-persist on the [data-panel-group] element to cover all panels in the group at once.

Global persist and opting out

init({ persist: true }) turns persistence on for every matching component. Two things keep that from being heavy-handed:

  • An id is required to store anything. A PanelSet writes under its container id; a Panel under its element id. A component without an id silently persists nothing, so a global flag only saves the components you’ve deliberately identified.
  • Any component can opt out. A per-element data-panel-persist="false" overrides the global option (attributes are the most specific setting). Inside a persisting [data-panel-group], a panel’s own data-panel-persist="false" also wins over the group.
HTML
<!-- Persists: inherits init({ persist: true }) and has an id-->
<div id="saved" data-panelset>
	<div class="panel-wrapper">...</div>
</div>

<!-- Never persists: no id to key on-->
<div data-panelset>
	<div class="panel-wrapper">...</div>
</div>

<!-- Opts out explicitly, even under a global persist-->
<div id="opted-out" data-panelset data-panel-persist="false">
	<div class="panel-wrapper">...</div>
</div>