--- meta: title: Split Panel description: Split panels display two adjacent panels, allowing the user to reposition them. layout: component --- ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; const App = () => (
Start
End
); ``` {% endraw %} ## Examples ### Initial Position To set the initial position, use the `position` attribute. If no position is provided, it will default to 50% of the available space. ```html:preview
Start
End
``` ### Initial Position in Pixels To set the initial position in pixels instead of a percentage, use the `position-in-pixels` attribute. ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; const App = () => (
Start
End
); ``` {% endraw %} ### Vertical Add the `vertical` attribute to render the split panel in a vertical orientation where the start and end panels are stacked. You also need to set a height when using the vertical orientation. ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; const App = () => (
Start
End
); ``` {% endraw %} ### Snapping To snap panels at specific positions while dragging, you can use the `snap` attribute. You can provide one or more space-separated pixel or percentage values, either as single values or within a `repeat()` expression, which will be repeated along the length of the panel. You can also customize how close the divider must be before snapping with the `snap-threshold` attribute. For example, to snap the panel at `100px` and `50%`, use `snap="100px 50%"`. ```html:preview
Start
End
``` Or, if you want to snap the panel to every `100px` interval, as well as at 50% of the panel's size, you can use `snap="repeat(100px) 50%"`. ```html:preview
Start
End
``` ### Using a Custom Snap Function You can also implement a custom snap function which controls the snapping manually. To do this, you need to acquire a reference to the element in Javascript and set the `snap` property. For example, if you want to snap the divider to either `100px` from the left or `100px` from the right, you can set the `snap` property to a function encoding that logic. ```js panel.snap = ({ pos, size }) => (pos < size / 2 ? 100 : size - 100); ``` Note that the `snap-threshold` property will not automatically be applied if `snap` is set to a function. Instead, the function itself must handle applying the threshold if desired, and is passed a `snapThreshold` member with its parameters. ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; const css = ` .split-panel-snapping { position: relative; } .split-panel-snapping-dots::before, .split-panel-snapping-dots::after { content: ''; position: absolute; bottom: -12px; width: 6px; height: 6px; border-radius: 50%; background: var(--sl-color-neutral-400); transform: translateX(-3px); } .split-panel-snapping-dots::before { left: 100px; } .split-panel-snapping-dots::after { left: 50%; } `; const App = () => ( <>
Start
End
); ``` {% endraw %} ### Disabled Add the `disabled` attribute to prevent the divider from being repositioned. ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; const App = () => (
Start
End
); ``` {% endraw %} ### Setting the Primary Panel By default, both panels will grow or shrink proportionally when the host element is resized. If a primary panel is designated, it will maintain its size and the secondary panel will grow or shrink to fit the remaining space. You can set the primary panel to `start` or `end` using the `primary` attribute. Try resizing the example below with each option and notice how the panels respond. ```html:preview
Start
End
None Start End
``` {% raw %} ```jsx:react import { useState } from 'react'; import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; import SlSelect from '@shoelace-style/shoelace/dist/react/select'; import SlMenuItem from '@shoelace-style/shoelace/dist/react/menu-item'; const App = () => { const [primary, setPrimary] = useState(''); return ( <>
Start
End
setPrimary(event.target.value)} > None Start End ); }; ``` {% endraw %} ### Min & Max To set a minimum or maximum size of the primary panel, use the `--min` and `--max` custom properties. Since the secondary panel is flexible, size constraints can only be applied to the primary panel. If no primary panel is designated, these constraints will be applied to the `start` panel. This examples demonstrates how you can ensure both panels are at least 150px using `--min`, `--max`, and the `calc()` function. ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; const App = () => (
Start
End
); ``` {% endraw %} ### Nested Split Panels Create complex layouts that can be repositioned independently by nesting split panels. ```html:preview
Start
Top
Bottom
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; const App = () => (
Start
Start
End
); ``` {% endraw %} ### Customizing the Divider You can target the `divider` part to apply CSS properties to the divider. To add a custom handle, slot an icon into the `divider` slot. When customizing the divider, make sure to think about focus styles for keyboard users. ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; import SlIcon from '@shoelace-style/shoelace/dist/react/icon'; const App = () => (
Start
End
); ``` {% endraw %} Here's a more elaborate example that changes the divider's color and width and adds a styled handle. ```html:preview
Start
End
``` {% raw %} ```jsx:react import SlSplitPanel from '@shoelace-style/shoelace/dist/react/split-panel'; import SlIcon from '@shoelace-style/shoelace/dist/react/icon'; const css = ` .split-panel-divider sl-split-panel { --divider-width: 2px; } .split-panel-divider sl-split-panel::part(divider) { background-color: var(--sl-color-pink-600); } .split-panel-divider sl-icon { position: absolute; border-radius: var(--sl-border-radius-small); background: var(--sl-color-pink-600); color: var(--sl-color-neutral-0); padding: .5rem .125rem; } .split-panel-divider sl-split-panel::part(divider):focus-visible { background-color: var(--sl-color-primary-600); } .split-panel-divider sl-split-panel:focus-within sl-icon { background-color: var(--sl-color-primary-600); color: var(--sl-color-neutral-0); } `; const App = () => ( <>
Start
End
); ``` {% endraw %}