DEV Community

loading...

How to Code Your Own Website Builder with React

LuisMPS
Interested in anything code related
・5 min read

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:

npm install --save build-ui
Enter fullscreen mode Exit fullscreen mode

Demo 🕹️

You can go check out this cool 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. (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

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:

const Alert = ({
    message,
    text,
    ...rest
}) => {
    const handleAlert = () => {
        alert(message);
    }
    return <button
        onClick = {handleAlert}
        {...rest}
    >
        {text}
    </button>
}
Enter fullscreen mode Exit fullscreen mode

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:

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>
}
Enter fullscreen mode Exit fullscreen mode

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):

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>
}
Enter fullscreen mode Exit fullscreen mode

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:

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>
}
Enter fullscreen mode Exit fullscreen mode

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!!

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>
}
Enter fullscreen mode Exit fullscreen mode

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 logo LuisMPS / build-ui

Code your own site builders with React

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.

Discussion (0)