How to Code Your Own Website Builder with React
Tags: react, webdev, websitebuilder, tutorial
Published: 2021-06-03 00:11:31

# Introduction

Wouldn't it be cool to code a website builder powered by **React**? 

*How would you even start?* 

It would be nice to have some sort of library upon which you could code your website editor with total freedom to implement whatever feature suiting your needs. Build UI lets you do just that.  🙌💪

# Overview 🔎

**Build-UI** is a library thought for React developers packed with features that allow you to effortlessly create website builders. It ships with drag and drop functionality, undo-redo logic, site serialization and deserialization along with some other interesting tools out of the box. Because of the way it is built, it also supports other features such as SSR and SSG, component dynamic loading, touch support for drag and drop functionality, and more, without you having to worry about hacking your way around.

# Installation 💻

You can install via npm with the command:

```shell
npm install --save build-ui
```

# Demo 🕹️

You can go check out this [cool demo](https://luismps.github.io/build-ui/demo/) we set up to show some of the features you can implement with Build UI.

# Tutorial

We'll create a very simple dummy website editor. For a better and more thorough tutorial you can refer to the [docs](https://luismps.github.io/build-ui/docs/tutorials/how-to-read). (I would still recommend going through the tutorial in the docs to understand Build UI better).

Behold, here's a preview of what we are going to be building:

![Tutorial GIF](https://i.imgur.com/bUPwQCV.gif)

It looks simple right? Trust me, the code is going to be simple too.

As you can see, we have a small Section where we can drop our Alert buttons from a single-button Toolbar. Simple, but it is essentially what all page builders do.

No more talking, let's jump into the code:

We'll start with that `Alert` Component:

```jsx
const Alert = ({
    message,
    text,
    ...rest
}) => {
    const handleAlert = () => {
        alert(message);
    }
    return <button
        onClick = {handleAlert}
        {...rest}
    >
        {text}
    </button>
}
```
All good, not much to see up here.

Now, with Build UI you create some special components named 'View Components', which wrap the components you add to your page (in this case our `Alert`). Let's see an example of a view component:

```jsx
import {DnDBuilder, useEditor} from 'build-ui';
import Alert from './Alert';

const AlertView = ({
    id,
    ...props
}) => {
    const editor = useEditor({
        id: id
    });
    return <DnDBuilder
        onDragStart = {editor.handleDragStart}
        onDragEnd = {editor.handleDragEnd}
        draggable = {true}
    >
        <Alert {...props} />
    </DnDBuilder>
}
```

So what's going on here?

As you can see, your view component will be injected with some props: an id and some props that your `Alert` Component will receive (Don't worry too much about this yet, we will later see more on this). In our View Component, we add that extra layer of logic needed for our website builder. This is great because it uses one of React's main features: *composition*.

Build UI ships with a special hook, called `useEditor`, that receives the id prop that you were passed in the component and in return, delivers a bag of useful functions that you can use for your site building logic. Here we defined a draggable view component, which means you can drag the component and drop it elsewhere. We could also define a view component to be a droppable component, where you can drop other components, with the `handleDrop` function we also receive from the editor. This is precisely what we do in our `SectionView`, with our `Section` that simply renders a div with some styles (Not recommending inline styles, by the way):

```jsx
const Section = props => {
    return <div 
        style = {{
            width: 600,
            height: 300,
            backgroundColor: '#eeebf2'
        }}
        {...props} 
    />
}

import {DnDBuilder, useEditor} from 'build-ui';
import Section from './Section';

const SectionView = ({
    id,
    ...props
}) => {
    const editor = useEditor({
        id: id
    });
    return <DnDBuilder
        onDrop = {editor.handleDrop}
    >
        <Section {...props} />
    </DnDBuilder>
}
```

You may have also noticed the `DnDBuilder` Component in which we wrapped our `Alert`. This is a component to which we pass drag-and-drop events. To us, it looks like a good old div element. In fact, it does render as a div element. But internally, `DnDBuilder` handles all drag and drop operations, including Touch Support, without us having to worry about its implementation.

Now, that Toolbar button down there... How it that coded? It uses Build UI's `useTools` hook. Build-UI supports drag and drop operations so you can interact with your site builder in that way. Let's take a look at the `AlertTools`:

```jsx
import {DnDBuilder, useTools, item, branch} from 'build-ui';

const AlertTools = () => {
    const tools = useTools();
    const handleDragTool = () => {
        const alertProps = {
            message: 'How is it going, folk?',
            text: 'Greet me',
        }
        const alert = item({
            type: 'Alert',
            props: alertProps
        });
        const data = branch(alert);
        tools.triggerDragStart({
            data: data,
        });
    }
    return <DnDBuilder
        onDragStart = {handleDragTool}
        onDragEnd = {tools.handleDragEnd}
        draggable = {true}
        as = 'button'
    >
        Alert
    </DnDBuilder>
}
```

Notice how we used a `branch` and `item` functions? Where did this came from and what are they used for? They are a couple of utility function shipped with Build-UI that allow you to define structures of what will be added to the site builder. This is where you define the type of what will be added to the builder, which should always be a string (most likely the name of your end component, like Alert), and the props your view component will be passed initially. Notice how we also used the triggerDragStart function to start dragging our `Alert` in our handler.

We are ready to look at what really glues everything together. It is the `Builder` Component, in which we will wrap our site builder.

We will kick off our initial website builder with the `Section` component we defined earlier where we will be able to drag stuff, such as hundreds of our alerts!!

```jsx
import {Builder, Workspace, item, branch} from "build-ui"
import AlertTools from "./ui/alert/AlertTools";
import AlertView from "./ui/alert/AlertView";
import SectionView from "./ui/section/SectionView";

const MyBuilder = () => {
    const section = item({
        type: 'Section',
        props: {},
    });
    const tree = branch(section);
    const view = {
        Section: SectionView,
        Alert: AlertView,
    }
    return <Builder initialTree = {tree}>
        <Workspace view = {view} />
        <AlertTools />
    </Builder>
}
```

Remember we used a type string when we created our `Alert` in the `AlertTools`? We do the same here for our the `Section` in our initial tree. And as you can probably notice, the view object above uses these type strings as keys, to know that to render to the screen.

Et voilà. We have finished creating a very (very) simple page editor where we can drag and drop limitless alerts to our page. How cool is that? Well, not much, really 😛😅. But we are sure you can come up with, and start building much more interesting page builders.

And... "where's the redo-undo, site serialization and all that other stuff you promised"? Take a look at the tutorial in the official documentation, you'll see how easy it is to use those features too.

# Repository 🗃️

Visit the repository to know more about the project. I will be glad to have you around 😊. You will find links to the documentation in the project's README.md. I will happy to receive any feedback.

{% github LuisMPS/build-ui no-readme %}

# Further Notes 📓

This is an early stage project, so any feedback/suggestions are welcome. If you would like to contribute to the project, contact me. It would be more than awesome to start a community.