Building a Trello Clone with Bryntum TaskBoard
Bryntum
Posted on August 15, 2023
Trello is one of the most commonly used 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 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 and Vue.js.
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 version 14 or newer as well as npm version 6 or newer installed on your computer. You'll also need a code editor like Visual Studio Code.
You can follow along with this tutorial using this GitHub repository.
Set Up the Project
To begin, run the following command to create a new React project _trello-with-bryntum_
using create-react-app
:
npx create-react-app trello-with-bryntum
Change the directory to the newly created project:
cd trello-with-bryntum
Once the directory has been updated, you need to disable the React StrictMode
to use Bryntum components, as this feature is currently under development. Remove <React.StrictMode>
from src/index.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. Bryntum serves the TaskBoard component from a private registry, so you need to specify the registry URL to npm
using the following command:
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:
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:
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 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:
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 thepublic/users/
folder.
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
:
// 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 ensureBryntumTaskBoard
works properly.
Run npm start
to start the application and open http://localhost:3000 in a web browser:
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 that come with it. Remove all existing styles from src/App.css
and import the material theme at the top of the file:
/* src/App.css */
@import '~@bryntum/taskboard/taskboard.material.css';
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:
// 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:
/* 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;
}
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:
// 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:
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 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:
// other code above
<BryntumTaskBoard
ref={taskBoard}
project={project}
columnDragFeature
{...taskBoardConfig}
/>
// example below
Another helpful feature is the TaskTooltip, which shows task details when hovering over a task. You can enable this by passing the taskTooltipFeature
prop to BryntumTaskBoard
:
// other code above
<BryntumTaskBoard
ref={taskBoard}
project={project}
columnDragFeature
taskTooltipFeature
{...taskBoardConfig}
/>
// example below
Conclusion
After completing this tutorial, you now have a Trello-like project management application that was created using the Bryntum TaskBoard. If you want to keep going, you can improve this application by utilizing other features from the TaskBoard documentation.
Bryntum 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, 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.
Posted on August 15, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.