Skip to content

Surfaces

Splitter

Bit.BlazorUISplitPaneResizable

The BitSplitter divides a container into two panels, side by side or stacked, with a gutter between them that the reader drags to give one panel more room and the other less. Each panel can declare a starting size and the minimum and maximum it is allowed to reach, the split is held as a percentage that survives the container being resized and can be bound, and the first panel can be folded away and brought back - by a control on the gutter, by the keyboard, by a drag that snaps it shut, or from code. A splitter can remember where it was left, report every stage of a resize, be held to a grid of its own, and move a line instead of the panels where the content is too heavy to lay out on every frame. The gutter is a WAI-ARIA window splitter: it takes the focus, moves with the arrow keys, and reports where it stands to a screen reader.

Usage

Every example is live. Open its code to see exactly what produced the component running underneath.

Basic

Everything in FirstPanel and SecondPanel is laid out on either side of the gutter, which starts halfway across the splitter. Drag the gutter to move the split, give it the focus and move it with the arrow keys (the whole keyboard is below), or double-click it to put the panels back where the parameters had them. A panel scrolls its own content, so neither side can push the other out of the splitter. A splitter fills whatever box it is given, so the height comes from the page around it - here, from the Style on the splitter itself.

First panel

Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams.
Second panel

Each word carried meaning, each pause brought understanding. The spaces here are open for growth.

Vertical

Vertical stacks the two panels instead of placing them side by side, and the gutter becomes a horizontal bar that moves with the up and down arrow keys. Everything else reads the same way round: the panel sizes and their minimums and maximums become heights, and the first panel is the one on top.

First panel

Once upon a time, stories wove connections between people, a symphony of voices crafting shared dreams.
Second panel

Each word carried meaning, each pause brought understanding. The spaces here are open for growth.

Panel size

FirstPanelSize and SecondPanelSize pin a panel to a size in pixels and let the other one take whatever is left, which is the layout of a sidebar beside a document. They are the size the splitter starts at: from the first drag on the split is held as a percentage, and a double-click on the gutter hands the layout back to them.

A first panel that starts at 150px
A second panel that takes the rest

A first panel that takes the rest
A second panel that starts at 150px

Min & max size

The four MinSize and MaxSize parameters, in pixels, are the range the gutter is held to - by the pointer, by the keyboard and by Percent alike. What one panel asks for bounds the other from the far side, so a minimum on the second panel is what keeps the first one from being dragged over it. The range is measured off the panels rather than read back from these parameters, so a minimum or maximum a stylesheet puts on a panel through Classes - as a percentage, or in any other unit - holds the gutter just as firmly.

Never narrower than 120px, never wider than 320px.
Never narrower than 100px, whatever the first panel does.

Percent

Percent is the whole layout in one number: the share of the splitter the first panel takes up. Being a share rather than a length, it keeps its proportions while the container is resized, and being bindable it works both ways - every drag, key press and collapse reports back through @bind-Percent, and writing to it moves the gutter. It takes precedence over the two panel sizes while it has a value.

30

First panel
Second panel

Drag step

DragStep is a grid, in pixels, that the split comes to rest on: the gutter follows the pointer but lands on the nearest multiple of it rather than wherever the pointer happens to be, which is what lines a panel up with a column of a grid, a tile or a row of text. The keyboard is held to the same multiples, so both ways of moving the gutter agree on where it is allowed to stop.

50

A panel that stops every 50 pixels
Second panel

Lazy resize

A splitter lays its content out again on every frame of a drag, which is what makes the panels follow the pointer - and what a panel holding a table of thousands of rows, an editor or a chart cannot keep up with. LazyResize is the way to drag one of those: a line moves instead of the panels, and they are laid out once, where the line was left. Everything else is unchanged - the keyboard, the minimum and maximum sizes, the snap to collapse and the events all behave as they do on any other splitter.

Drag the gutter: the line moves first, the panels follow when you let go.
Second panel

Keyboard

The gutter is a tab stop of its own and behaves as the WAI-ARIA window splitter pattern asks. The arrow keys that lie along the splitter move it by KeyboardStep pixels - ten by default - and hold it inside the minimum and maximum sizes of both panels; Page Up, Page Down and an arrow key held with Shift move it ten steps at once; Home and End take it to the smallest and the largest size the panels allow; Escape abandons a drag that is under way, putting the panels back exactly as it found them. On a collapsible splitter Enter folds the first panel away and brings it back, and so does Ctrl with the arrow key that points at it or away from it. A double-click hands the layout back to the panel size parameters unless NoResetOnDoubleClick takes that away.

One arrow key moves the gutter 50px.
Shift, Page Up and Page Down move it 500px.

Double-clicking the gutter
leaves this splitter where the reader put it.

Collapsible

Collapsible lets the first panel be folded away: press Enter or Ctrl with an arrow key on the gutter, drag it close enough to the start of the splitter for it to snap shut, or press the gutter of an already folded panel to open it again - and a control on the gutter can offer the same to the pointer. CollapsedSize is the sliver it is left at: a folded panel keeps its content in the DOM and drops the minimum size it holds while it is open, so it can close past a minimum that would otherwise stop it. Collapsed is bindable, so the page always knows, and expanding puts the panel back at the size it had before it was folded rather than at some default.

A panel that can be folded away
Collapsed: False

Without a word on the subject, a drag closes the panel once it has taken it under half of its minimum size - or under a twentieth of the splitter, where there is no minimum. SnapSize names that distance instead: here the panel only shuts inside the first 40 pixels, so the whole of the range above it stays draggable.

Drag the gutter to the first 40px to fold this away.
Second panel

CollapseSecondPanel moves all of that to the other side of the gutter, which is what an inspector, a properties pane or a preview at the far end of the splitter needs. Nothing else changes its meaning: Collapsed, CollapsedSize, SnapSize, the keys and the methods all go on describing the one panel that folds, and Percent still describes the first panel - so the split the second panel comes back to is the one it left.

A document that takes the whole splitter once the inspector is away
An inspector that folds to the right

Collapse button

ShowCollapseButton draws the fold on the gutter itself. Without it the only ways in are a key press and a drag that happens to reach the edge, neither of which a reader can see is on offer; with it the gutter says so. The chevron points at what the next press will do and turns with the orientation of the splitter and the writing direction of the page, and CollapseIconName and ExpandIconName - or CollapseIcon and ExpandIcon, for an external icon set - put something else there. It is the pointer's way to what the gutter already answers to from the keyboard, so it stays out of the tab order and out of the accessibility tree: a screen reader is handed the separator and the fold it offers once, not twice.

Press the chevron to fold this away.
Second panel

A stacked splitter folds upwards.
Second panel

Icons of the page's own choosing
CollapseIconName and ExpandIconName

Cancelling a collapse

OnCollapsing is asked before the panel moves, with what is about to happen and what asked for it - the gutter, a drag that snapped it shut, one of the methods, or the remembered position being restored. Setting Cancel on the arguments leaves the panel exactly as it is, which is how a fold is made to wait for a confirmation or a save. The callback is awaited, and nothing else folds the panel while it is running.


A panel that folds away only with permission
Second panel

Nothing has been folded yet.

Programmatic control

A reference to the component gives the same moves to the page: SetPercent puts the gutter at a share of the splitter (clamped to 0-100, and still held to the minimum and maximum sizes of both panels), GetPercent reads the share the first panel takes up at this moment - measured off the panels, so it answers even for a split nobody has moved yet, which Percent does not hold until the first drag - ResetSize hands the layout back to the panel size parameters, Collapse, Expand and ToggleCollapse fold the collapsible panel away and back - none of them turned away by Collapsible, which is about what the reader may do to the gutter - and FocusAsync puts the keyboard on the gutter. A splitter whose Percent or Collapsed the page holds one way keeps what the page gave it: the state is the page's, and these methods do not take it back.


First panel
Second panel

Nothing has been measured yet.

Events

OnResizeStart opens a resize and exactly one of OnResizeEnd and OnResizeCancel closes it - the second of those when Escape, or the browser taking the pointer away, puts the panels back where the resize found them - so whatever a page sets aside for the duration of one always has somewhere to pick it up again. OnResize reports the resize as it happens, one call per frame of a drag and one per key press; each of the four carries the share of the splitter the first panel takes up. OnCollapsedChange reports the panel being folded away and brought back, and OnGutterDoubleClick the gutter being double-clicked, whether or not that also resets the splitter. A splitter with no handler for OnResize makes no call at all while it is being dragged, so leave it unset where only the final position matters.

First panel
Second panel

No resize yet.

Persistence

PersistKey has the splitter remember where the gutter was left - and whether the first panel was folded away - so the reader finds the layout they arranged the last time they were here. It is kept in the browser's local storage under that key, or in the session storage instead with PersistInSessionStorage, and the key has to be unique to the splitter within the origin: two splitters sharing one key share one position. What comes back is offered the way a drag is, so a page holding Percent or Collapsed one way keeps what it declared. Move the gutter below and reload the page.

A panel that is where you left it
Second panel

Read-only & disabled

ReadOnly is a split that is not up for negotiation: the splitter looks exactly as it does anywhere else, but the gutter cannot be dragged or moved from the keyboard. IsEnabled="false" takes the resize away too and dims the whole component with it. Both leave the gutter out of the tab order and hand it back to a screen reader as the plain rule it then is rather than as a control with a position; the public methods still work in either case.

Read-only
The gutter stays where it is.

Disabled
The whole splitter is dimmed.

Nested

A splitter is ordinary content, so putting one in a panel of another gives the three-pane layout of an inbox or an editor. Each of them keeps its own sizes and its own gutter.

The first panel of the outer splitter
The first panel of the nested splitter
The second panel of the nested splitter

Gutter size

GutterSize is the thickness of the gutter in pixels. However thin it is drawn, the strip that answers a press is widened evenly on both sides up to GutterHitSize - 24 pixels by default, and 44 wherever the pointer is a coarse one - so a hairline gutter is still something a finger can find, and none of that reaches into the layout: the panels give up no room for it. A gutter already thicker than the floor is left exactly as it is.

10

First panel
Second panel

Gutter icon

GutterIconName puts a built-in Fluent UI icon in the gutter in place of the default grip, which gives the reader something to aim at on a wider gutter.

First panel
Second panel

Gutter template

GutterTemplate replaces what is drawn inside the gutter altogether. The gutter itself is the separator - the thing the pointer drags and the keyboard moves - so what goes in here is decoration: a control of its own in there would be a second tab stop on something a screen reader reports as a single separator.

First panel
Second panel

External Icons

Use icons from external libraries like FontAwesome and Bootstrap Icons with the GutterIcon parameter (BitIconInfo).

FontAwesome:

First panel
"fa-solid fa-arrows-left-right"

First panel
BitIconInfo.Css("fa-solid fa-grip-vertical")

First panel
BitIconInfo.Fa("solid grip-lines-vertical")


Bootstrap:

First panel
GutterIcon=@("bi bi-grip-vertical")

First panel
BitIconInfo.Bi("arrow-left-right")

Style & Class

Style and Class dress the root element - which is where the height of a splitter is set, since it fills whatever box it is put in - while Styles and Classes reach the parts inside it: the two panels, the gutter, the icon or grip drawn in the gutter, the collapse control and its icon, and the line a lazy drag moves. A minimum or maximum a class puts on a panel holds the gutter just as firmly as the parameters do, and in any unit, because the range is measured off the panels rather than read back from C#.


Component's Style & Class:

A splitter with a Style of its own
Second panel
A splitter with a Class of its own
Second panel


Styles & Classes:

Panels and gutter painted
through the Styles slots
Panels and gutter painted
through the Classes slots

RTL

In a right-to-left splitter the first panel is the one on the right, and the gutter follows: dragging it to the left makes the first panel bigger, and so does the arrow key that points away from it.

پنل اول
پنل دوم

API

Every parameter, public member, sub-class and enum this component exposes.

BitSplitter parameters

Name Type Default value Description
Classes BitSplitterClassStyles? null Custom CSS classes for different parts of the BitSplitter.
CollapseIcon BitIconInfo? null The icon of the collapse button while the panel that folds is open, using BitIconInfo for external icon library support. Takes precedence over CollapseIconName when both are set.
CollapseIconName string? null The name of the built-in Fluent UI icon shown on the collapse button while the panel that folds is open. The default is a chevron pointing at the panel that is about to be folded away, which follows the orientation of the splitter, which panel folds and the writing direction of the page.
Collapsed bool false Whether the panel that folds - the first one, or the second where CollapseSecondPanel says so - is currently collapsed. It can be bound, so a collapse the user carries out on the gutter is reported back to the page. A collapsed panel keeps its content in the DOM and is folded down to CollapsedSize, ignoring the minimum size it would otherwise hold, while the panel left standing takes the whole splitter.
CollapsedSize int 0 The size, in pixels, the folded panel is held at while it is collapsed.
Collapsible bool false Lets a panel be collapsed - the first one, or the second where CollapseSecondPanel says so: pressing Enter on the gutter folds it away and opens it again, so does Ctrl with an arrow key and the control ShowCollapseButton draws, dragging the gutter close enough to that panel's own edge of the splitter snaps it shut, and Collapse/Expand/ToggleCollapse do the same from code.
CollapseSecondPanel bool false Folds the second panel away rather than the first one, which is what an inspector, a properties pane or a preview sitting at the far end of the splitter needs. It moves what Collapsible offers to the other side of the gutter and nothing else: Collapsed, CollapsedSize, SnapSize, the keys, the collapse control and the Collapse/Expand/ToggleCollapse methods all go on meaning the one panel that folds, and Percent still describes the first panel.
DragStep int 0 The grid, in pixels, a drag of the gutter moves the split along: the first panel comes to rest on a multiple of this rather than wherever the pointer happens to be. The keyboard is held to the same multiples. The default of 0 is no grid at all.
ExpandIcon BitIconInfo? null The icon of the collapse button while the panel that folds is away, using BitIconInfo for external icon library support. Takes precedence over ExpandIconName when both are set.
ExpandIconName string? null The name of the built-in Fluent UI icon shown on the collapse button while the panel that folds is away. The default is a chevron pointing at the room the panel is about to come back into.
FirstPanel RenderFragment? null The content for the first panel.
FirstPanelSize int? null The initial size of the first panel in pixels. From the first drag on, the split is held as a percentage in Percent, which takes precedence over this and over SecondPanelSize.
FirstPanelMaxSize int? null The max size of first panel in pixels.
FirstPanelMinSize int? null The min size of first panel in pixels.
GutterHitSize int? null The smallest strip, in pixels, a pointer has to land in to take hold of the gutter. A gutter drawn narrower than this still answers a press that lands within the strip, which reaches past what is drawn evenly on both sides without taking any room from the panels. The default is 24, the target size WCAG asks for, and 44 wherever the pointer is a coarse one.
GutterIcon BitIconInfo? null The icon for the BitSplitter gutter using BitIconInfo for external icon library support. Takes precedence over GutterIconName when both are set.
GutterIconName string? null The name of the built-in Fluent UI icon to render in the BitSplitter gutter. Ignored when GutterIcon is also set.
GutterSize int? null The size of BitSplitter gutter in pixels.
GutterTemplate RenderFragment? null The custom content of the gutter, in place of the icon or of the default grip indicator. The gutter is the separator itself, so what goes in here is decoration rather than a control.
KeyboardStep int 10 How far, in pixels, one press of an arrow key on the gutter moves the split. Page Up and Page Down, and an arrow key held with Shift, move it ten of these steps at a time; Home and End take it all the way to the smallest and the largest size the panels allow.
LazyResize bool false Moves a line rather than the panels while the gutter is being dragged, and puts the panels where it was left only once the drag is over. It is what makes a panel holding a table of thousands of rows, an editor or a chart draggable: the cost of the drag becomes a single layout at its end.
NoResetOnDoubleClick bool false Keeps the gutter from resetting the splitter to the sizes its parameters declare when it is double-clicked.
OnCollapsedChange EventCallback<bool> The callback invoked when the panel that folds is collapsed or expanded.
OnCollapsing EventCallback<BitSplitterCollapseArgs> The callback invoked before the panel that folds is collapsed or expanded, with what is about to happen and what asked for it. Set Cancel on the arguments to leave the panel as it is. The callback is awaited, and nothing else folds the panel while it is running.
OnGutterDoubleClick EventCallback The callback invoked when the gutter is double-clicked, whether or not the double-click also resets the splitter.
OnResize EventCallback<double> The callback invoked continuously while the gutter is being dragged, with the new share of the splitter the first panel takes up, as a percentage. It is coalesced to one call per animation frame, and a splitter with no handler for it makes no interop call at all while it is being dragged.
OnResizeCancel EventCallback<double> The callback invoked when a resize is abandoned rather than finished - by Escape, or by the browser taking the pointer away - with the share of the splitter the first panel is put back to. Exactly one of this and OnResizeEnd follows every OnResizeStart.
OnResizeEnd EventCallback<double> The callback invoked when a resize has finished, with the share of the splitter the first panel ended up taking, as a percentage.
OnResizeStart EventCallback<double> The callback invoked when a resize starts, with the share of the splitter the first panel takes up at that moment, as a percentage.
PersistKey string? null The key the splitter remembers its position under, so that a reader who has moved the gutter finds it where they left it the next time the page is opened. Both the position and whether the first panel was folded away are kept, and what is restored is offered to the component the way a drag is. The key has to be unique to the splitter within the origin.
PersistInSessionStorage bool false Keeps what PersistKey remembers in the browser's session storage rather than its local storage, so the position lasts as long as the tab and no longer.
Percent double? null The share of the splitter the first panel takes up, as a percentage between 0 and 100. It survives the container being resized and can be bound, so every drag, key press and collapse is reported back to the page. While it has a value it takes precedence over FirstPanelSize and SecondPanelSize.
ReadOnly bool false Keeps the splitter as it is: the gutter is still shown and still looks like itself, but it cannot be dragged or moved from the keyboard.
SecondPanel RenderFragment? null The content for the second panel.
SecondPanelSize int? null The initial size of the second panel in pixels. Ignored while Percent has a value, which is the case from the first drag on.
SecondPanelMaxSize int? null The max size of second panel in pixels.
SecondPanelMinSize int? null The min size of second panel in pixels.
ShowCollapseButton bool false Draws a control on the gutter that folds the collapsible panel away and brings it back, so that a reader working with the pointer can see that the panel folds at all. Only a Collapsible splitter has one. It is the pointer's way to what the gutter already does from the keyboard, so it stays out of the tab order and out of the accessibility tree.
SnapSize int 0 How small, in pixels, a drag has to leave the panel that folds for it to snap shut instead of staying open. Only a Collapsible splitter snaps at all. The default of 0 leaves the splitter to work it out from the minimum size of the panel: half of it, or a twentieth of the splitter where there is no minimum.
Styles BitSplitterClassStyles? null Custom CSS styles for different parts of the BitSplitter.
Vertical bool false Sets the orientation of BitSplitter to vertical, stacking the two panels instead of placing them side by side.

BitSplitter public members

Name Type Default value Description
Collapse Task Collapses the panel that folds. Does nothing if it is already collapsed. Not turned away by Collapsible, which is about what the reader may do to the gutter.
Expand Task Expands the panel that folds, putting the split back where it was before the fold.
ToggleCollapse Task Collapses the panel that folds if it is expanded and expands it if it is collapsed.
SetPercent Task Moves the split so that the first panel takes up the given share of the splitter, as a percentage between 0 and 100. The value is still held to the minimum and maximum sizes of both panels.
GetPercent ValueTask<double?> Reads the share of the splitter the first panel takes up at this moment, as a percentage, measured off the panels themselves rather than read back from Percent. It is the only way to a split nobody has moved yet, which Percent does not hold until the first drag. A splitter whose JavaScript side is not up, and one laid out in a box with no room in it, answer null.
ResetSize Task Clears Percent and hands the layout back to FirstPanelSize and SecondPanelSize - which is what a double-click on the gutter does.
FocusAsync ValueTask Gives the focus to the gutter, which is the control a splitter is driven by. The overload taking a bool prevents the gutter from being scrolled into view.

BitComponentBase parameters

Name Type Default value Description
AriaLabel string? null Gets or sets the accessible label for the component, used by assistive technologies.
Class string? null Gets or sets the CSS class name(s) to apply to the rendered element.
Dir BitDir? null Gets or sets the text directionality for the component's content.
ForceAnimation bool false Gets or sets a value indicating whether the component's animations play at their full duration even when reduced motion is requested.
HtmlAttributes Dictionary<string, object> new Dictionary<string, object>() Captures additional HTML attributes to be applied to the rendered element, in addition to the component's parameters.
Id string? null Gets or sets the unique identifier for the component's root element.
IsEnabled bool true Gets or sets a value indicating whether the component is enabled and can respond to user interaction.
Style string? null Gets or sets the CSS style string to apply to the rendered element.
TabIndex string? null Gets or sets the tab order index for the component when navigating with the keyboard.
Visibility BitVisibility BitVisibility.Visible Gets or sets the visibility state (visible, hidden, or collapsed) of the component.

BitComponentBase public members

Name Type Default value Description
UniqueId Guid Guid.NewGuid() Gets the readonly unique identifier for the component's root element, assigned when the component instance is constructed.
RootElement ElementReference Gets the reference to the root HTML element associated with this component.

BitSplitterCollapseArgs properties

Name Type Default value Description
IsCollapsing bool The state the panel that folds is about to move to: true while it is being folded away, false while it is being brought back.
Reason BitSplitterCollapseReason What made the panel collapse or expand: the gutter, a drag that snapped it shut, a call to one of the Collapse, Expand and ToggleCollapse methods, or the remembered position being restored.
Cancel bool false Set to true to cancel the collapse or the expansion and leave the panel as it is.

BitSplitterClassStyles properties

Name Type Default value Description
Root string? null The custom CSS class/style for the root element of the BitSplitter.
CollapseButton string? null The custom CSS class/style for the control on the gutter that folds the first panel of the BitSplitter away and brings it back.
CollapseButtonIcon string? null The custom CSS class/style for the icon of the control on the gutter that folds the first panel of the BitSplitter away and brings it back.
FirstPanel string? null The custom CSS class/style for the first panel of the BitSplitter.
Gutter string? null The custom CSS class/style for the gutter (the separator) of the BitSplitter.
GutterIcon string? null The custom CSS class/style for the icon rendered inside the gutter of the BitSplitter.
GutterIndicator string? null The custom CSS class/style for the default grip indicator rendered inside the gutter of the BitSplitter.
Preview string? null The custom CSS class/style for the line a lazy drag moves in place of the panels of the BitSplitter.
SecondPanel string? null The custom CSS class/style for the second panel of the BitSplitter.

BitIconInfo properties

Name Type Default value Description
Name string? null Gets or sets the name of the icon.
BaseClass string? null Gets or sets the base CSS class for the icon. For built-in Fluent UI icons, this defaults to "bit-icon". For external icon libraries like FontAwesome, you might set this to "fa" or leave empty.
Prefix string? null Gets or sets the CSS class prefix used before the icon name. For built-in Fluent UI icons, this defaults to "bit-icon--". For external icon libraries, you might set this to "fa-" or leave empty.

BitSplitterCollapseReason enum

Name Value Description
Gutter 0 The gutter was pressed, or moved by the Enter key or Ctrl with an arrow key.
Drag 1 The gutter was dragged close enough to the panel's own edge of the splitter for it to snap shut.
Method 2 The Collapse, Expand or ToggleCollapse method of the splitter was called.
Restore 3 The position the splitter had remembered under its PersistKey was restored.

BitVisibility enum

Name Value Description
Visible 0 The content of the component is visible.
Hidden 1 The content of the component is hidden, but the space it takes on the page remains (visibility:hidden).
Collapsed 2 The component is hidden (display:none).

BitDir enum

Name Value Description
Ltr 0 Ltr (left to right) is to be used for languages that are written from the left to the right (like English).
Rtl 1 Rtl (right to left) is to be used for languages that are written from the right to the left (like Arabic).
Auto 2 Auto lets the user agent decide. It uses a basic algorithm as it parses the characters inside the element until it finds a character with a strong directionality, then applies that directionality to the whole element.

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.