Vue-Splitpanes

A Vue.js reliable, simple and touch-ready panes splitter / resizer.

Features

Github project  &  important notes

1
I have a min width of 20%
2
3
4
5
<splitpanes style="height: 400px">
  <pane min-size="20">1</pane>
  <pane>
    <splitpanes horizontal>
      <pane>2</pane>
      <pane>3</pane>
      <pane>4</pane>
    </splitpanes>
  </pane>
  <pane>5</pane>
</splitpanes>
.splitpanes__pane {
  display: flex;
  justify-content: center;
  align-items: center;
  font-family: Helvetica, Arial, sans-serif;
  color: rgba(255, 255, 255, 0.6);
  font-size: 5em;
}

Installation

You have two options: NPM or <script> tag.

Via NPM

npm i @tenrok/vue-splitpanes@legacy # For Vue 2.x.
npm i @tenrok/vue-splitpanes # For Vue 3.

View and edit a workingVue 3 example on Codepen.

Then import the component and CSS:
// In your Vue component.
import { Splitpanes, Pane } from '@tenrok/vue-splitpanes'
import '@tenrok/vue-splitpanes/dist/splitpanes.css'

export default {
  components: { Splitpanes, Pane },
  ...
}

Via <script> tag

Include the Splitpanes script in your document <head> as follows:

<head>
  ...
  <script src="https://unpkg.com/vue"></script>
  <script src="https://unpkg.com/vue-splitpanes"></script>
  <link href="https://unpkg.com/vue-splitpanes/dist/splitpanes.css" rel="stylesheet">
</head>

How to use

Once included in your project, use as follows.

<splitpanes class="default-theme">
  <pane v-for="i in 3" :key="i">
    <div>{{ i }}</div>
  </pane>
</splitpanes>

No splitter tags!
The splitters will be added automatically between the <pane> tags.

By default the layout is vertical, if you need you can set the attribute horizontal on the <splitpanes> tag to change the layout to rows.

The CSS is external so you can easily override or choose not to include it at all.
If you want to use it, you can also optionally use the CSS class default-theme at the root of your splitpanes to apply the default theme like on this page.
If you want to go with your own style, you can check the Do Your Own Style example.

More examples

Vue 3

View and edit a working Vue 3 example on Codepen

Horizontal layout, push other panes, min & max use

You can also double click a splitter to maximize the next pane! (First pane splitter will be an option soon)

If you want to disable the 'double click splitter to maximize' behavior, you can add this attribute: :dbl-click-splitter="false".

1
I have a min height of 20% & max height of 70%
2
3
I have a max height of 70%
<splitpanes class="default-theme" horizontal style="height: 400px">
  <pane min-size="20" max-size="70">
    <span>1</span>
  </pane>
  <pane>
    <span>2</span>
  </pane>
  <pane max-size="70">
    <span>3</span>
  </pane>
</splitpanes>

Default pane width or height

Provide dimension of your panes when they first load (will be used for the width or height respectively for the vertical or horizontal layout).
If you provide a default width or height, make sure you provide it for all the panes and the total equals 100%.
If a pane is missing a default width or height, then all the panes will have the same width or height.
Note that setting a default value is different than setting a min or max value.

1
2
3
<splitpanes class="default-theme" horizontal style="height: 400px">
  <pane size="65">
    <span>1</span>
  </pane>
  <pane size="10">
    <span>2</span>
  </pane>
  <pane size="25">
    <span>3</span>
  </pane>
</splitpanes>

Mix layout with nested splitpanes & prevent pushing other panes

Try it yourself on Codepen

1
2
3
4
5
<splitpanes class="default-theme" horizontal :push-other-panes="false" style="height: 400px">
  <pane>
    <span>1</span>
  </pane>
  <pane>
    <splitpanes :push-other-panes="false">
      <pane>
        <span>2</span>
      </pane>
      <pane>
        <span>3</span>
      </pane>
      <pane>
        <span>4</span>
      </pane>
    </splitpanes>
  </pane>
  <pane>
    <span>5</span>
  </pane>
</splitpanes>

Lots of splitters & push other panes - all panes have a min width of 5%

1
2
3
4
5
6
7
8
<splitpanes class="default-theme" style="height: 400px">
  <pane v-for="i in 8" :key="i" min-size="5">
    <span>{{ i }}</span>
  </pane>
</splitpanes>

Adding splitters on the fly

This example shows the reactivity when you add a new element dynamically in splitpanes.

1
2
3
<button @click="panesNumber++">Add pane</button>
<button @click="panesNumber--">Remove pane</button>

<splitpanes class="default-theme" style="height: 400px">
  <pane v-for="i in panesNumber" :key="i">
    <span>{{ i }}</span>
  </pane>
</splitpanes>
// In your Vue component.
data: () => ({
  panesNumber: 3
})

Change direction & first splitter

When changing direction, all the panes current width or height will flip to adapt to the new layout.

Showing the first splitter is an option which allows user to double click the splitter to maximize the next pane.
The first splitter does not allow to resize the next pane.

1
2
3
<button @click="horizontal = !horizontal">Switch to {{ horizontal ? 'Vertical' : 'Horizontal' }}</button>
<button @click="firstSplitter = !firstSplitter">{{ firstSplitter ? 'Hide' : 'Show' }} First Splitter</button>

<splitpanes class="default-theme" :horizontal="horizontal" :first-splitter="firstSplitter" style="height: 400px">
  <pane v-for="i in 3" :key="i">
    <span>{{ i }}%</span>
  </pane>
</splitpanes>
data: () => ({
  horizontal: false
  firstSplitter: false
})

Programmatic resizing

This example shows the programmatic way of resizing panes. And how it works both ways.

50%
50%
<w-slider v-model="paneSize" label="First pane size" :min="0" :max="100">
<splitpanes class="default-theme" @resize="paneSize = $event[0].size" style="height: 400px">
  <pane :size="paneSize">
    <span>{{ paneSize }}%</span>
  </pane>
  <pane :size="100 - paneSize">
    <span>{{ 100 - paneSize }}%</span>
  </pane>
</splitpanes>
// In your Vue component.
data: () => ({
  paneSize: 50
})

In-depth reactivity

This example shows the reactivity when you modify anything in your component inside splitpanes.

1
Number is: 0
2
Number is: 0
Number on the left is: 0
Number on the right is: 0
3
Number is: 0
4
- Nested splitpanes -
[0, 0, 0]
<button @click="generateRandomNumber">Generate 3 random numbers</button>
<button @click="incrementNumber(3)">Increment pane #3</button>

<splitpanes horizontal class="default-theme" style="height: 400px">
  <pane>
    <splitpanes>
      <pane v-for="i in 3" :key="i">
        <span>{{ i }}</span><br>
        <em>Number is: {{ randomNums[i] }}</em><br>
        <em v-if="i === 2">
          Number on the left is: {{ randomNums[1] }}<br>
          Number on the right is: {{ randomNums[3] }}<br>
        </em>
        <button(v-if="i !== 2" @click="randomNums[i] = randomNums[i] + 1">+1</button>
      </pane>
    </splitpanes>
  </pane>
  <pane>
    <span>4</span><br>
    <em>
      - Nested splitpanes -<br>
      [{{ randomNums[1] }}, {{ randomNums[2] }}, {{ randomNums[1] }}]
    </em>
  </pane>
</splitpanes>
// In your Vue component.
data: () => ({
  randomNums: { 1: 0, 2: 0, 3: 0 }
}),
methods: {
  generateRandomNumber () {
    this.randomNums = Object.assign(this.randomNums, {
      1: Math.round(Math.random() * 100),
      2: Math.round(Math.random() * 100),
      3: Math.round(Math.random() * 100)
    })
  },
  incrementNumber (i) {
    this.randomNums[i]++
  }
}

Toggle a pane with v-if

1
2
3
<button @click="hidePane2 = !hidePane2">{{ hidePane2 ? 'Show' : 'Hide' }} Pane 2</button>
<splitpanes class="default-theme" style="height: 400px">
  <pane>
    <span>1</span>
  </pane>
  <pane v-if="!hidePane2">
    <span>2</span>
  </pane>
  <pane>
    <span>3</span>
  </pane>
</splitpanes>

Vue Router inside splitpanes

This is another reactivity example of a rather common case: Vue Router inside splitpanes.
The navigation is in the left pane, but you can also access from outside of splitpanes, through those buttons:

Home viewAnother view
I have a min width of 20%
router-view
3
<button to="home-view">Home view</button>
<button to="another-view">Another view</button>

<splitpanes horizontal class="default-theme" style="height: 400px">
  <pane min-size="20">
    <p>Navigation</p>
    <ul>
      <li><router-link to="home-view">Home view</li>
      <li><router-link to="another-view">Another view</li>
    </ul>
  </pane>
  <pane>
    <em>router-view</em>
    <router-view />
  </pane>
  <pane>
    <span>3</span>
  </pane>
</splitpanes>
// Vue Router routes.
routes: [
  {
    path: '/home-view',
    component: () => import(/* webpackChunkName: "home-view" */ './components/home-view.vue')
  },
  {
    path: '/another-view',
    component: () => import(/* webpackChunkName: "another-view" */ './components/another-view.vue')
  }
]
<template>
  <div class="green">
    <div>This is home</div>
  </div>
</template>

Listening to emitted events

Here is the list of events that are emitted from splitpanes:

Try resizing panes and check the logs bellow.

1
2
3
// Event name: Event params   (Last event on top)
ready: 
<splitpanes
  class="default-theme"
  @resize="log('resize', $event)"
  @resized="log('resized', $event)"
  @pane-maximize="log('pane-maximize', $event)"
  @pane-click="log('pane-click', $event)"
  @ready="log('ready', $event)"
  @splitter-click="log('splitter-click', $event)"
  style="height: 400px">
  <pane v-for="i in 3" :key="i" min-size="10">
    <span>{{ i }}</span>
  </pane>
</splitpanes>

Increased reactive touch zone for touch devices

Try it yourself on Codepen

1
2
3

In this example the splitters are thin lines but the reactive touch zone is spread to 30 pixels all around! Hover a splitter to see the enlarged fat-finger-proof reactive zone.

<splitpanes horizontal style="height: 400px">
  <pane>
    <splitpanes>
      <pane>
        <span>1</span>
      </pane>
      <pane>
        <span>2</span>
      </pane>
      <pane>
        <span>3</span>
      </pane>
    </splitpanes>
  </pane>
  <pane>
    <p>In this example the splitters are thin lines but the reactive touch zone is spread to 30 pixels all around!</p>
  </pane>
</splitpanes>
.splitpanes {background-color: #f8f8f8;}

.splitpanes__splitter {background-color: #ccc;position: relative;}
.splitpanes__splitter:before {
  content: '';
  position: absolute;
  left: 0;
  top: 0;
  transition: opacity 0.4s;
  background-color: rgba(255, 0, 0, 0.3);
  opacity: 0;
  z-index: 1;
}
.splitpanes__splitter:hover:before {opacity: 1;}
.splitpanes--vertical > .splitpanes__splitter:before {left: -30px;right: -30px;height: 100%;}
.splitpanes--horizontal > .splitpanes__splitter:before {top: -30px;bottom: -30px;width: 100%;}

Do your own style

If you don't want to use the default style, here is how to do your own.

Try it yourself on Codepen

1
2
3
4
<splitpanes horizontal style="height: 400px">
  <pane>
    <splitpanes vertical>
      <pane>
        <span>1</span>
      </pane>
      <pane>
        <span>2</span>
      </pane>
      <pane>
        <span>3</span>
      </pane>
    </splitpanes>
  </pane>
  <pane>
    <span>4</span>
  </pane>
</splitpanes>
.splitpanes {
  background: linear-gradient(-45deg, #EE7752, #E73C7E, #23A6D5, #23D5AB);
}

.splitpanes__pane {
  box-shadow: 0 0 5px rgba(0, 0, 0, .2) inset;
  justify-content: center;
  align-items: center;
  display: flex;
}

.splitpanes--vertical > .splitpanes__splitter {
  min-width: 6px;
  background: linear-gradient(90deg, #ccc, #111);
}

.splitpanes--horizontal > .splitpanes__splitter {
  min-height: 6px;
  background: linear-gradient(0deg, #ccc, #111);
}

API

Here is the list of all the props.

Release Notes

Version 2.3.5 Prevent splitter double taps on touch devices if `dblClickSplitter` is set to false.
Version 2.3.4 Fix removing pane DOM nodes in IE11
Version 2.3.1 Fix firing `pane-click` event on pane click
Version 2.3.0 Support rtl direction
Version 2.2.0
Version 2.0.0 Fix reactivity issues.

  • Children now must be wrapped into a `pane` component.
  • The attribute `splitpanes-size` is now replaced with `size` on the `pane` component.
  • you can still add CSS classes on the `pane` component tag.

Version 1.14.0 Programmatically set pane size
Version 1.13.0 Emit event on splitter click
Version 1.12.0 double click splitter to maximize is now an option
Version 1.11.0 Persist panes size after slots changed
Version 1.10.0 Add maximum size feature on panes
Version 1.9.0 Emit event on resize & watch slots optional

The `resize` event - previously firing after resize end - is now firing on resize.
A new `resized` event is emitted on resize end. Check out the Listening to emitted events example.

By default and for performance, the reactivity is now limited to slot deletion and slot creation.
With the option `watchSlots` you can also track any change on the slots.

Version 1.8.0 Watch slots
Version 1.7.0 Double click splitter to maximize next pane
Version 1.6.0 Emit events
Version 1.5.0 Add default size feature on panes
Version 1.4.0 Add minimum size feature on panes
Version 1.3.0 Splitpanes slots are now reactive (add/remove on the fly)
Version 1.2.0 Add a `default-theme` CSS class to load default theme
Version 1.1.0 Allow pushing other panes while dragging splitter
Version 1.0.0 First public release