Skip to content

Multi-floor buildings ​

One plan can hold a whole building: basements, ground floor and any number of floors above. Everything stays in one JSON document, and every component knows which floor it's showing.

The hotel below has four floors. Pick a floor with the buttons on the right, and switch between Floor, Building and Exploded at the bottom right.

Loading the live demo…

Floors in the editor ​

The floor menu sits at the left of the toolbar in the top-right corner. It shows the floor you're editing, for example G Ground floor.

ActionHow
Switch floorPick it in the floor menu, or press Page Up / Page Down
Add a floorAdd floor above / Add floor below in the floor menu
Rename, set heightFloor settings in the floor menu
Copy a floorFloor settings → Duplicate floor: copies the walls, rooms, doors and furniture onto a new floor on top. Perfect for hotels and offices with repeating floors.
ReorderFloor settings → Move up / Move down swaps the floor with its neighbour
DeleteFloor settings → Delete floor (a building always keeps at least one floor)

While you edit a floor, the floor below is drawn faintly underneath as a guide, so outer walls, stairs and lift shafts line up. Undo and redo cover floor changes too.

vue
<FloorPlanEditor v-model="plan" v-model:floor="floorId" />
tsx
<FloorplanEditor plan={plan} onChange={setPlan} floor={floorId} onFloorChange={setFloorId} />
html
<floorplan-editor></floorplan-editor>
<script type="module">
  const editor = document.querySelector('floorplan-editor')
  editor.plan = plan
  editor.floor = floorId
  editor.addEventListener('floor-change', (e) => (floorId = e.detail.floor))
</script>

The floor binding is optional. Use it to start on a particular floor or to keep the floor in sync with the rest of your page.

Floors in the 2D viewer ​

When a plan has more than one floor, a column of floor buttons appears in the top-right corner. The view keeps its position when you switch floors, because all floors share the same coordinates.

  • Markers show on their own floor (see Markers).
  • plan-click, marker-move and marker-drag include the floor the event happened on.
  • focusMarker(id) and focusRoom(id) switch to the right floor automatically.
vue
<FloorPlanViewer :plan="plan" :markers="markers" v-model:floor="floorId" @plan-click="({ x, y, floor }) => …" />
tsx
<FloorplanViewer
  plan={plan}
  markers={markers}
  floor={floorId}
  onFloorChange={setFloorId}
  onPlanClick={({ x, y, floor }) => …}
/>
html
<floorplan-viewer></floorplan-viewer>
<script type="module">
  const viewer = document.querySelector('floorplan-viewer')
  viewer.plan = plan
  viewer.markers = markers
  viewer.floor = floorId
  viewer.addEventListener('floor-change', (e) => (floorId = e.detail.floor))
  viewer.addEventListener('plan-click', (e) => {
    const { x, y, floor } = e.detail
  })
</script>

Set showFloorSwitcher to false to hide the buttons and drive the floor from your own UI (:show-floor-switcher="false" in Vue, showFloorSwitcher={false} in React, viewer.showFloorSwitcher = false on the element).

Try it in 2D: switch floors with the buttons on the right, and notice the view stays where it is.

Loading the live demo…

The whole building in 3D ​

The 3D viewer has three ways to show a multi-floor building. Pick one with the buttons at the bottom right or the mode prop:

ModeWhat you see
Floor ('floor')Only the selected floor.
Building ('stacked', default)The real building: floors stacked at their heights. Floors above the selected one fade to a light wireframe, so you can look straight into the selected floor. Pick another floor and the camera moves up or down with it.
Exploded ('exploded')Floors pulled apart vertically and viewed from a lower angle, so every floor is visible at once. Every floor's markers show; room labels are hidden to keep the overview clean. Clicking a floor selects it.

Cutaway works in every mode. In Building mode it slices the selected floor at about 1.1 m.

vue
<FloorPlanViewer3D :plan="plan" :markers="markers" v-model:floor="floorId" mode="exploded" />

<!-- or keep the mode in sync both ways -->
<FloorPlanViewer3D :plan="plan" v-model:floor="floorId" v-model:mode="mode" />
tsx
<FloorplanViewer3D plan={plan} markers={markers} floor={floorId} onFloorChange={setFloorId} mode="exploded" />

// or keep the mode in sync both ways
<FloorplanViewer3D plan={plan} floor={floorId} onFloorChange={setFloorId} mode={mode} onModeChange={setMode} />
html
<floorplan-viewer-3d mode="exploded"></floorplan-viewer-3d>
<script type="module">
  import '@jbaysolutions/floorplan-elements/3d'

  const viewer3d = document.querySelector('floorplan-viewer-3d')
  viewer3d.plan = plan
  viewer3d.markers = markers
  viewer3d.floor = floorId
  viewer3d.addEventListener('floor-change', (e) => (floorId = e.detail.floor))
  viewer3d.addEventListener('mode-change', (e) => console.log(e.detail.mode)) // 'floor' | 'stacked' | 'exploded'
</script>

Heights ​

Each floor has its own floor-to-floor height (height, default 280 cm), so a tall lobby or a low basement is easy. In 3D, walls are the floor height minus the 20 cm slab, and each floor sits on the one below.

See Data model for the Floor fields and the floor helpers (sortedFloors, defaultFloor, floorElevations…).

JBay Floorplan is free for open source and companies under €1M yearly revenue. Licensing and pricing