Building a Trello Clone with Bryntum TaskBoard
Tags: javascript, tutorial, productivity, react
Published: 2023-08-15 10:38:54

[Trello](https://trello.com/) is one of the [most commonly used](https://venturebeat.com/business/work-management-platform-trello-introduces-no-code-automation-features/#:~:text=which%20has%20over%2050%20million%20users) project management applications. And why shouldn't it be? With its intuitive design and easy-to-use interface, Trello makes project management effortless. You can easily drag and drop tasks and move them to different columns.

But what if there was a solution where you could build your own Trello clone in minutes? The [Bryntum TaskBoard](https://www.bryntum.com/docs/taskboard/) can do just that. It's a perfect component that provides the features you can use to create a project management application like Trello. It has a pure JavaScript implementation that you can integrate with popular frontend libraries and frameworks, including [React](https://reactjs.org/) and [Vue.js](https://vuejs.org/).

In this tutorial, you'll learn how to build a project management application like Trello using React and Bryntum TaskBoard. You'll start with a basic React application and work through the code examples to create a fully functional project management application.

## Building a Trello-like Application Using Bryntum TaskBoard

Before you begin this tutorial, you'll need [Node.js](https://nodejs.org/en/) version 14 or newer as well as [npm](https://www.npmjs.com/) version 6 or newer installed on your computer. You'll also need a code editor like [Visual Studio Code](https://code.visualstudio.com/).

You can follow along with this tutorial using this [GitHub repository](https://github.com/Anshuman71/trello-with-bryntum).

### Set Up the Project

To begin, run the following command to create a new React project `_trello-with-bryntum_` using `create-react-app`:

```bash
npx create-react-app trello-with-bryntum
```

Change the directory to the newly created project:

```bash
cd trello-with-bryntum
```

Once the directory has been updated, you need to disable the [React `StrictMode`](https://reactjs.org/docs/strict-mode.html) to use Bryntum components, as this feature is currently [under development](https://www.bryntum.com/forum/viewtopic.php?p=108599#p108599). Remove `<React.StrictMode>` from `src/index.js`:

```js
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    <App />
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: <https://bit.ly/CRA-vitals>
reportWebVitals();
```

### Install Bryntum TaskBoard

To install Bryntum TaskBoard, you need to sign up for a [free trial](https://www.bryntum.com/download/). Bryntum serves the TaskBoard component from a private registry, so you need to specify the registry URL to `npm` using the following command:

```bash
npm config set "@bryntum:registry=https://npm.bryntum.com"
```

Since Bryntum's registry is authenticated, you'll need to log in using the credentials you signed up with:

```bash
npm login --registry=https://npm.bryntum.com
```

> **Please note:** The "@" symbol should be replaced with ".." in the login email. For example, "someone@example.com" will become "someone..example.com". Use "trial" as the password.

Now, run the following command to install the Bryntum TaskBoard dependencies:

```bash
npm i @bryntum/taskboard@npm:@bryntum/taskboard-trial@^5.1.2 @bryntum/taskboard-react@5.1.2
```

### Add TaskBoard Configuration

To add TaskBoard configuration, create a new folder `public/users/` inside the project. Then download the [user images](https://github.com/Anshuman71/trello-with-bryntum/tree/master/public/users) and add them to this folder. This allows the application to access these images even in a production environment.

Create a new file called `src/AppConfig.js` and export the `taskBoardConfig` object that provides a list of `column` names, the column name mapping field `status`, and the path to resource images:

```js
export const taskBoardConfig = {
  // Url for resource avatar images
  resourceImagePath: "users/",
  // default columns
  columns: ["todo", "doing", "done"],
  // Field used to pair a task to a column
  columnField: "status",
};
```

Then create a new file called `src/AppData.js` and export the application's default data. This example uses a static file, but you can also use an API endpoint or database to fetch this if you prefer:

> **Note:** The `user.image` should match a file name from the `public/users/` folder.

```js
export const projectData = {
  tasks: [
    { id: 1, name: "Book flight", status: "done", prio: "medium" },
    { id: 2, name: "Book hotel", status: "done", prio: "medium" },
    { id: 3, name: "Pack bags", status: "doing", prio: "low" },
    { id: 4, name: "Get visa", status: "doing", prio: "high" },
    { id: 5, name: "Book train", status: "done", prio: "medium" },
    { id: 6, name: "Go to airport", status: "todo", prio: "low" },
    { id: 7, name: "Renew passport", status: "todo", prio: "high" },
    { id: 8, name: "Swim in pool", status: "todo", prio: "medium" },
    { id: 9, name: "Scuba diving", status: "todo", prio: "medium" },
    { id: 10, name: "Canyoning", status: "todo", prio: "low" },
    { id: 11, name: "Snorkeling", status: "doing", prio: "medium" },
    { id: 12, name: "Diving license", status: "todo", prio: "medium" },
    { id: 13, name: "Book cab", status: "done", prio: "low" },
    { id: 14, name: "Write postcards", status: "todo", prio: "medium" },
    { id: 15, name: "Take pictures", status: "todo", prio: "low" },
    { id: 16, name: "Take selfies", status: "todo", prio: "high" },
    { id: 17, name: "Post on instagram", status: "todo", prio: "medium" },
    { id: 18, name: "Call grandma", status: "todo", prio: "medium" },
    { id: 19, name: "Buy swimming ring", status: "done", prio: "high" },
    { id: 20, name: "Get in shape", status: "doing", prio: "medium" },
    { id: 21, name: "Iron shirts", status: "done", prio: "low" },
  ],
  users: [
    { id: 1, name: "Angelo", image: "angelo.jpg" },
    { id: 2, name: "Celia", image: "celia.jpg" },
    { id: 3, name: "Dave", image: "dave.jpg" },
    { id: 4, name: "Emilia", image: "emilia.jpg" },
    { id: 5, name: "Gloria", image: "gloria.jpg" },
    { id: 6, name: "Henrik", image: "henrik.jpg" },
    { id: 7, name: "Kate", image: "kate.jpg" },
    { id: 8, name: "Lee", image: "lee.jpg" },
    { id: 9, name: "Lisa", image: "lisa.jpg" },
    { id: 10, name: "Mark", image: "mark.jpg" },
    { id: 11, name: "Steve", image: "steve.jpg" },
  ],
  assignments: [
    { id: 1, event: 7, resource: 1 },
    { id: 2, event: 7, resource: 2 },
    { id: 3, event: 8, resource: 2 },
    { id: 4, event: 4, resource: 3 },
    { id: 5, event: 7, resource: 3 },
    { id: 6, event: 7, resource: 4 },
    { id: 7, event: 7, resource: 5 },
    { id: 8, event: 7, resource: 6 },
    { id: 9, event: 7, resource: 7 },
    { id: 10, event: 7, resource: 8 },
    { id: 11, event: 7, resource: 9 },
    { id: 12, event: 7, resource: 10 },
    { id: 13, event: 7, resource: 11 },
    { id: 14, event: 16, resource: 7 },
    { id: 15, event: 16, resource: 8 },
    { id: 16, event: 16, resource: 9 },
    { id: 17, event: 16, resource: 10 },
    { id: 18, event: 16, resource: 11 },
    { id: 19, event: 19, resource: 10 },
    { id: 20, event: 9, resource: 7 },
    { id: 21, event: 12, resource: 8 },
    { id: 22, event: 14, resource: 9 },
    { id: 23, event: 17, resource: 10 },
    { id: 24, event: 18, resource: 10 },
    { id: 25, event: 11, resource: 9 },
    { id: 26, event: 20, resource: 8 },
    { id: 27, event: 1, resource: 7 },
    { id: 28, event: 2, resource: 6 },
    { id: 29, event: 5, resource: 5 },
    { id: 30, event: 6, resource: 4 },
    { id: 31, event: 10, resource: 3 },
    { id: 32, event: 15, resource: 2 },
    { id: 33, event: 3, resource: 1 },
    { id: 34, event: 13, resource: 2 },
    { id: 35, event: 21, resource: 3 },
    { id: 36, event: 8, resource: 3 },
    { id: 37, event: 17, resource: 9 },
    { id: 38, event: 17, resource: 8 },
    { id: 39, event: 17, resource: 7 },
    { id: 40, event: 17, resource: 6 },
  ],
};

```

### Use Bryntum TaskBoard

Now that the project configuration is ready, it's time to use the `BryntumTaskBoard` and `BryntumProjectModel` components inside the `src/App.js` to create a drag-and-drop project manager.

Import the `taskBoardConfig` from `./AppConfig.js`, and `projectData` from the `./AppData.js` files. Use the `useRef` hook to create a reference to the components and the `useState` hook to create individual states for the `projectData`:

```jsx
// src/App.js
import React, { useRef, useState } from "react";
import {
  BryntumTaskBoard,
  BryntumProjectModel,
} from "@bryntum/taskboard-react";
import { taskBoardConfig } from "./AppConfig";
import { projectData } from "./AppData";
import "./App.css";

function App() {
  const taskBoard = useRef();
  const project = useRef();
  const [tasks] = useState(projectData.tasks);
  const [assignments] = useState(projectData.assignments);
  const [resources] = useState(projectData.users);
  return (
    <>
      <BryntumProjectModel
        ref={project}
        tasks={tasks}
        assignments={assignments}
        resources={resources}
      />
      <BryntumTaskBoard
        ref={taskBoard}
        project={project}
        {...taskBoardConfig}
      />
    </>
  );
}

export default App;
```

> **Please note:** You must use the `BryntumProjectModel` as the first component in the tree to ensure `BryntumTaskBoard` works properly.

Run `npm start` to start the application and open [http://localhost:3000](http://localhost:3000) in a web browser:

![TaskBoard without styling](https://i.imgur.com/KxTxpV8.png)

At this point, the TaskBoard component will render, but it doesn't look like a board yet. To render the TaskBoard component correctly, you need to use one of the [many themes](https://www.bryntum.com/docs/taskboard/guide/TaskBoard/customization/styling#picking-a-theme) that come with it. Remove all existing styles from `src/App.css` and import the material theme at the top of the file:

```css
/* src/App.css */
@import '~@bryntum/taskboard/taskboard.material.css';
```

![TaskBoard with material theme](https://i.imgur.com/D11UJdx.png)

#### Add New Column Functionality

Now that the basic TaskBoard is ready, you need to add a header at the top with a button to add a new column. The button will use the `prompt` API to ask for the new column name and then add the new column by using the `taskBoard` ref and calling the `columns.add` method on it:

```jsx
// src/App.js
import React, { useRef, useState } from "react";
import {
  BryntumTaskBoard,
  BryntumProjectModel,
} from "@bryntum/taskboard-react";
import { taskBoardConfig } from "./AppConfig";
import { projectData } from "./AppData";
import "./App.css";

function App() {
  const taskBoard = useRef();
  const project = useRef();
  const [tasks] = useState(projectData.tasks);
  const [assignments] = useState(projectData.assignments);
  const [resources] = useState(projectData.users);
  return (
    <>
      <BryntumProjectModel
        ref={project}
        tasks={tasks}
        assignments={assignments}
        resources={resources}
      />
      <nav className={"header"}>
        <h1 className={"heading"}>Trello using Bryntum TaskBoard</h1>
        <button
          className={"add-column"}
          onClick={() => {
            const value = prompt("Column name");
            if (value) {
              taskBoard.current.instance.columns.add({
                id: value,
                text: value,
              });
            }
          }}
        >
          Add column
        </button>
      </nav>
      <BryntumTaskBoard
        ref={taskBoard}
        project={project}
        {...taskBoardConfig}
      />
    </>
  );
}

export default App;
```

Add the following styles to `App.css` to style the header similar to Trello:

```css
/* src/App.css */
@import '~@bryntum/taskboard/taskboard.material.css';

* {
  margin: 0px;
  box-sizing: border-box;
}

.header {
  display: flex;
  padding: 20px;
  justify-content: space-between;
  align-items: center;
  color: white;
  background-color: dodgerblue;
}

.header .heading {
  font-size: 24px;
}

.header .add-column {
  padding: 5px 10px;
  border-radius: 5px;
  border: none;
  font-size: 16px;
  cursor: pointer;
}
```

![TaskBoard with header](https://i.imgur.com/H0RvX3R.png)

You can add the `onChange` event listener on the `BryntumProjectModel` to update the `tasks` state when the project data changes. The `onChange` handler will receive an object describing the change, and you can use the `change.action` to check if a new task is added or an existing one is updated:

```jsx
// src/App.js
import React, { useRef, useState } from "react";
import {
  BryntumTaskBoard,
  BryntumProjectModel,
} from "@bryntum/taskboard-react";
import { taskBoardConfig } from "./AppConfig";
import { projectData } from "./AppData";
import "./App.css";

function App() {
  const taskBoard = useRef();
  const project = useRef();
  const [tasks, setTasks] = useState(projectData.tasks);
  const [assignments] = useState(projectData.assignments);
  const [resources] = useState(projectData.users);
  function onChange(change) {
    if(change.action === 'add') {
      setTasks(() => [...tasks, change.records[0].data])
    }
    if(change.action === 'update') {
      const updatedTask =  change.record.data
      const newTasks = [...tasks].filter(item => item.id !== updatedTask.id)
      setTasks(() => [...newTasks, updatedTask])
    }
  }

  return (
    <>
      <BryntumProjectModel
        ref={project}
        tasks={tasks}
        assignments={assignments}
        resources={resources}
        onChange={onChange}
      />
      <nav className={"header"}>
        <h1 className={"heading"}>Trello using Bryntum TaskBoard</h1>
        <button
          className={"add-column"}
          onClick={() => {
            const value = prompt("Column name");
            if (value) {
              taskBoard.current.instance.columns.add({
                id: value,
                text: value,
              });
            }
          }}
        >
          Add column
        </button>
      </nav>
      <BryntumTaskBoard
        ref={taskBoard}
        project={project}
        {...taskBoardConfig}
      />
    </>
  );
}

export default App;

```

And that's it! You've successfully created a Trello-like project management application using Bryntum TaskBoard. This application provides features such as using drag and drop for tasks and dynamically adding columns out-of-the-box:

![TaskBoard in action](https://i.imgur.com/CycbZMk.gif)

### Additional Functionality

Your basic Trello clone is ready, but with Bryntum, you can add even more functionality to improve the user experience with just a few props.

For instance, to enable [moving columns](https://www.bryntum.com/docs/taskboard/api/TaskBoard/feature/ColumnDrag) using drag-and-drop functionality, you just need to pass the `columnDragFeature` prop to the `BryntumTaskBoard` to enable it. Now users can quickly move the columns around in the order they want:

```jsx
     // other code above
	 <BryntumTaskBoard
        ref={taskBoard}
        project={project}
        columnDragFeature
        {...taskBoardConfig}
      />
	 // example below
```

![TaskBoard column drag and drop](https://i.imgur.com/B4aWrMh.gif)

Another helpful feature is the [TaskTooltip](https://www.bryntum.com/docs/taskboard/api/TaskBoard/feature/TaskTooltip), which shows task details when hovering over a task. You can enable this by passing the `taskTooltipFeature` prop to `BryntumTaskBoard`:

```jsx
	// other code above
	<BryntumTaskBoard
        ref={taskBoard}
        project={project}
        columnDragFeature
		taskTooltipFeature
        {...taskBoardConfig}
    />
	// example below
```

![Task details tooltip](https://i.imgur.com/WgyHmQg.png)

## Conclusion

After completing this tutorial, you now have a Trello-like project management application that was created using the [Bryntum TaskBoard](https://www.bryntum.com/docs/taskboard). If you want to keep going, you can improve this application by utilizing other features from the [TaskBoard documentation](https://bryntum.com/docs/taskboard/guide/TaskBoard/integration/react/guide#features).

[Bryntum](https://www.bryntum.com) is a software company that creates beautiful and performant web components for professional usage, including TaskBoard, Scheduler, and Gantt. Bryntum's web components come with thorough [documentation](https://www.bryntum.com/docs/), making them easy to integrate with popular frontend frameworks, including React and Vue.js.

Bryntum helps the world stay on schedule. Our component library offers a selection of high-quality, advanced UI controls for various frontend frameworks. Want to learn more? Try our scheduling and Gantt components at [bryntum.com](https://www.bryntum.com/).