Skip to content

Your first page ​

An editor to draw a building, and a viewer that reports where people click.

vue
<script setup lang="ts">
import { ref } from 'vue'
import { FloorPlanEditor, FloorPlanViewer, createEmptyPlan, type PlanClickEvent } from '@jbaysolutions/floorplan-vue'

const plan = ref(createEmptyPlan('Head office')) // a building with one ground floor

function onClick(e: PlanClickEvent) {
  console.log(`Clicked at ${e.x}, ${e.y} cm on ${e.floor.name}`, e.room?.name)
}
</script>

<template>
  <div style="height: 600px">
    <FloorPlanEditor v-model="plan" />
  </div>

  <div style="height: 400px">
    <FloorPlanViewer :plan="plan" @plan-click="onClick" />
  </div>
</template>
tsx
import { useState } from 'react'
import { FloorplanEditor, FloorplanViewer, createEmptyPlan, type PlanClickEvent } from '@jbaysolutions/floorplan-react'

export function Office() {
  const [plan, setPlan] = useState(() => createEmptyPlan('Head office')) // a building with one ground floor

  function onClick(e: PlanClickEvent) {
    console.log(`Clicked at ${e.x}, ${e.y} cm on ${e.floor.name}`, e.room?.name)
  }

  return (
    <>
      <FloorplanEditor plan={plan} onChange={setPlan} style={{ height: 600 }} />
      <FloorplanViewer plan={plan} onPlanClick={onClick} style={{ height: 400 }} />
    </>
  )
}
html
<floorplan-editor style="height: 600px"></floorplan-editor>
<floorplan-viewer style="height: 400px"></floorplan-viewer>

<script type="module">
  import { createEmptyPlan } from '@jbaysolutions/floorplan-elements'

  const editor = document.querySelector('floorplan-editor')
  const viewer = document.querySelector('floorplan-viewer')

  editor.plan = viewer.plan = createEmptyPlan('Head office') // a building with one ground floor

  // the editor sends the new plan after every edit: show it in the viewer too
  editor.addEventListener('change', (e) => (viewer.plan = e.detail.plan))

  viewer.addEventListener('plan-click', (e) => {
    const { x, y, room, floor } = e.detail
    console.log(`Clicked at ${x}, ${y} cm on ${floor.name}`, room?.name)
  })
</script>

Give the components a height

Every component fills the box it's given (minimum 240 px). In Vue, give the parent a height; in React and on the elements, give the component itself a height (style or CSS).

Try the editor below: draw a room with the room tool, then watch the event the editor sends.

Loading the live demo…
Draw or change something: the new plan is emitted after every edit.

More than one floor? ​

A plan is a whole building. Add floors from the floor menu in the editor (top right), and both viewers show a floor switcher automatically. See Multi-floor buildings.

Saving and loading ​

The plan is plain JSON. Save it as it is, and pass it back in later through normalizePlan, which fills in anything missing and upgrades plans saved by older versions:

ts
import { normalizePlan } from '@jbaysolutions/floorplan-vue'

await api.savePlan(JSON.stringify(plan.value))
plan.value = normalizePlan(JSON.parse(await api.loadPlan()))
tsx
import { normalizePlan } from '@jbaysolutions/floorplan-react'

await api.savePlan(JSON.stringify(plan))
setPlan(normalizePlan(JSON.parse(await api.loadPlan())))
js
import { normalizePlan } from '@jbaysolutions/floorplan-elements'

await api.savePlan(JSON.stringify(editor.plan))
editor.plan = normalizePlan(JSON.parse(await api.loadPlan()))

See Data model for the format.

Next steps ​

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