This Halloween I invite you to create something both functional and fun! In this tutorial, we’ll build a Halloween-themed task manager using SVAR React Gantt, a powerful, open-source project management component that can easily handle serious enterprise workloads… but today, we’re giving it a spooky twist.
The demo app will track the ultimate October project — organizing a Halloween party. You’ll plan tasks, set dates, and assign responsibilities, so you can later know who’s to blame if the candy runs out 😝
You’ll learn how to use SVAR React Gantt to:
- Add a Gantt chart and fill it with data
- Customize the task editor
- Add a context menu and tooltips
- Customize the default theme
And while it’s fun to play with pumpkins and ghosts, everything here translates directly to production-grade project management apps.
Useful links:
- Online demo: see the Halloween-styled Gantt chart demo
- Demo package: download the demo package
- GitHub: find the source code of SVAR React Gantt
Including Sources
We start by installing SVAR Gantt Chart source files via npm:
npm install @svar-ui/react-gantt
Adding Gantt Chart on a Page
Import SVAR Gantt, the CSS styles, and the desired theme. The task manager comes with light and dark skins: Willow and WillowDark. To give the chart a spooky styling, in our tutorial we’ll use WillowDark theme as a basis.
Component.jsx
import { Gantt, WillowDark } from "@svar-ui/react-gantt";
import "@svar-ui/react-gantt/all.css";
Add the Gantt on page and define a theme for it:
Component.jsx
import { Gantt, WillowDark } from "@svar-ui/react-gantt";
import "@svar-ui/react-gantt/all.css";
function Component() {
return (
<WillowDark>
<Gantt />
</WillowDark>
);
}
export default Component;
Filling Gantt Chart with Data
To bring our task manager to life, we need to provide the initial data for tasks and their links that form the project's structure at the initialization stage. The data includes four key elements: scales (timeline units), tasks (project activities with dates and progress), links (dependencies between tasks), and users (assignees for tasks).
data/index.js
const dayStyle = (date) => {
const day = date.getDay() === 5 || date.getDay() === 6;
return day ? 'sday' : '';
};
export const scales = [
{ unit: 'month', step: 1, format: 'MMMM yyy' },
{ unit: 'day', step: 1, format: 'd', css: dayStyle },
];
export const tasks = [
{
id: 1,
start: new Date(2025, 9, 19),
end: new Date(2025, 9, 25),
text: 'Planning & Preparation',
progress: 100,
parent: 0,
type: 'summary',
open: true,
details: '',
},
{
id: 11,
start: new Date(2025, 9, 19),
end: new Date(2025, 9, 20),
text: 'Set date',
progress: 100,
parent: 1,
type: 'task',
details:
"Finalize the date and time for the Halloween party. Make sure it suits everyone's availability",
to: 1,
},
// ... Other task data
];
export const links = [
{
id: 1,
source: 11,
target: 13,
type: 'e2s',
},
// ... Other link data
];
export const users = [
{ id: 1, label: 'David' },
{ id: 2, label: 'Olivia' },
];
export function getData() {
return { tasks, links, scales, users };
}
Since we keep our data on client side in a separate file, we need to import it and pass the corresponding data items to the Gantt component as props:
Component.jsx
import { useMemo, useState } from 'react';
import { Gantt, WillowDark } from "@svar-ui/react-gantt";
import "@svar-ui/react-gantt/all.css";
import { getData } from '../data';
function Component() {
const [api, setApi] = useState();
const data = useMemo(() => getData(), []);
return (
<WillowDark>
<Gantt
init={setApi}
tasks={data.tasks}
links={data.links}
scales={data.scales}
/>
</WillowDark>
);
}
export default Component;
For more information about preparing and loading data, refer to the following documentation section: Loading data.
Customizing Task Tree
The Gantt widget includes a left-hand panel that displays a task tree, providing users with an overview of project details. By default, this tree features four columns: Task Name, Start Date, Duration, and Action. While this layout provides detailed insights, it can also take up extra screen space, which is especially valuable in busy project views.
To create a more compact interface, we can adjust the task tree structure using the columns property. If we exclude the default Duration column, we can free up space on the screen, allowing for a cleaner view that keeps the focus on the most critical information: task name, start date, and the + button to add a new task.
Component.jsx
import { useMemo, useState } from 'react';
import { Gantt, WillowDark } from "@svar-ui/react-gantt";
import "@svar-ui/react-gantt/all.css";
import { getData } from '../data';
function Component() {
const [api, setApi] = useState();
const data = useMemo(() => getData(), []);
// Define the new structure of the Task Tree
const columns = [
{ id: 'text', header: 'Task name', width: 210 },
{
id: 'start',
header: 'Start date',
width: 106,
align: 'center',
},
{ id: 'add-task', header: '', width: 40, align: 'center' },
];
return (
<WillowDark>
<Gantt
init={setApi}
tasks={data.tasks}
links={data.links}
scales={data.scales}
columns={columns}
/>
</WillowDark>
);
}
export default Component;
Adding Task Editor
The default editor of the Gantt chart provides a straightforward way for users to edit tasks and save changes directly on the chart. However, for our Halloween-themed project tracker, we'll enhance the standard editor by adding a custom field to assign responsibilities to each task.
Component.jsx
import { useMemo, useState } from 'react';
import {
Gantt,
WillowDark,
Editor,
defaultEditorItems // Import the default settings of the Gantt editor
} from "@svar-ui/react-gantt";
import "@svar-ui/react-gantt/all.css";
import { getData } from '../data';
function Component() {
const [api, setApi] = useState();
const data = useMemo(() => getData(), []);
const columns = [
{ id: 'text', header: 'Task name', width: 210 },
{
id: 'start',
header: 'Start date',
width: 106,
align: 'center',
},
{ id: 'add-task', header: '', width: 40, align: 'center' },
];
// Reshape the default editor settings to exclude unnecessary fields
const editorItems = defaultEditorItems.filter(
(op) => op.key !== 'duration' && op.key !== 'links'
);
// Add a custom field to assign the responsible user
editorItems.splice(3, 0, {
key: 'to',
type: 'select',
label: 'Goes to',
options: data.users,
config: { placeholder: 'Select the person' },
comp: 'select',
});
return (
<WillowDark>
<Gantt
init={setApi}
tasks={data.tasks}
links={data.links}
scales={data.scales}
columns={columns}
/>
{api && <Editor api={api} autoSave={false} items={editorItems} />}
</WillowDark>
);
}
export default Component;
For more information about configuring the task editor, refer to the following documentation section: Configuring task editor.
Adding Context Menu
SVAR Gantt component uses ContextMenu component as a context menu. The menu can be packed with a wide range of options for managing tasks: editing, deleting, creating subtasks, and much more. While this full menu offers extensive functionality, our Halloween Gantt app requires a more compact, streamlined set of options to keep the interface clean and focused.
Let's customize the context menu and select only the essential options that suit our demo app's needs.
Component.jsx
import { useMemo, useState } from 'react';
import {
Gantt,
WillowDark,
Editor,
ContextMenu, // Import the built-in context menu
defaultMenuOptions, // Import default menu options
defaultEditorItems
} from "@svar-ui/react-gantt";
import "@svar-ui/react-gantt/all.css";
import { getData } from '../data';
function Component() {
const [api, setApi] = useState();
const data = useMemo(() => getData(), []);
const columns = [
{ id: 'text', header: 'Task name', width: 210 },
{
id: 'start',
header: 'Start date',
width: 106,
align: 'center',
},
{ id: 'add-task', header: '', width: 40, align: 'center' },
];
const editorItems = defaultEditorItems.filter(
(op) => op.key !== 'duration' && op.key !== 'links'
);
editorItems.splice(3, 0, {
key: 'to',
type: 'select',
label: 'Goes to',
options: data.users,
config: { placeholder: 'Select the person' },
comp: 'select',
});
// Specify a set of available options within a custom context menu
const ids = ['add-task', 'edit-task', 'delete-task', 'move-task'];
// Reshape the default menu options considering the custom options
const options = defaultMenuOptions.filter((op) => ids.indexOf(op.id) >= 0);
return (
<WillowDark>
<ContextMenu api={api} options={options}>
<Gantt
init={setApi}
tasks={data.tasks}
links={data.links}
scales={data.scales}
columns={columns}
/>
</ContextMenu>
{api && <Editor api={api} autoSave={false} items={editorItems} />}
</WillowDark>
);
}
export default Component;
Please note that to activate the context menu features, you need to access Gantt API and pass it to the <ContextMenu api={api}> component. In this case we use the init prop to capture the API reference when Gantt initializes.
For more information on how to configure the context menu, refer to the following documentation section: Configuring the context menu.
Adding Tooltips
By default, the SVAR Gantt chart doesn't show tooltips. But we can easily activate this feature. All we need is to import the Tooltip component and wrap our Gantt component with it. Do not forget to provide the API reference as with the ContextMenu.
This simple setup will allow users to hover the pointer over a task or project and see a tooltip with the task's info.
Component.jsx
import { useMemo, useState } from 'react';
import {
Gantt,
WillowDark,
Editor,
ContextMenu,
Tooltip, // Import the built-in tooltip
defaultMenuOptions,
defaultEditorItems
} from "@svar-ui/react-gantt";
import "@svar-ui/react-gantt/all.css";
import { getData } from '../data';
function Component() {
const [api, setApi] = useState();
const data = useMemo(() => getData(), []);
const columns = [
{ id: 'text', header: 'Task name', width: 210 },
{
id: 'start',
header: 'Start date',
width: 106,
align: 'center',
},
{ id: 'add-task', header: '', width: 40, align: 'center' },
];
const editorItems = defaultEditorItems.filter(
(op) => op.key !== 'duration' && op.key !== 'links'
);
editorItems.splice(3, 0, {
key: 'to',
type: 'select',
label: 'Goes to',
options: data.users,
config: { placeholder: 'Select the person' },
comp: 'select',
});
const ids = ['add-task', 'edit-task', 'delete-task', 'move-task'];
const options = defaultMenuOptions.filter((op) => ids.indexOf(op.id) >= 0);
return (
<WillowDark>
<ContextMenu api={api} options={options}>
<Tooltip api={api}>
<Gantt
init={setApi}
tasks={data.tasks}
links={data.links}
scales={data.scales}
columns={columns}
/>
</Tooltip>
</ContextMenu>
{api && <Editor api={api} autoSave={false} items={editorItems} />}
</WillowDark>
);
}
export default Component;
For our Halloween-themed app, though, a simple title won't capture the full story. To make task details more accessible and engaging, we'll create a custom tooltip that displays the task description and assigned user.
First of all, we need to create a custom template for a tooltip. For convenience, we can put it in a separate file.
TooltipContent.jsx
import { users } from '../data'; // Import data for USERS to be displayed in a tooltip
export default function TooltipContent({ data }) {
if (!data) return null;
const { to, details } = data;
return (
<>
{to || details ? (
<div className="data">
{details ? ( // Create a view for details (if exist)
<div className="text">
<span className="caption">🎃 Description: </span>
<span className="details">{details}</span>
</div>
) : null}
{to ? ( // Create a view for assignee (if exists)
<div className="text">
<span className="caption">👻 Goes to: </span>
{users.find((u) => u.id === to)?.label}
</div>
) : null}
</div>
) : null}
</>
);
}
TooltipContent.css
.data {
background-color: #2b343b;
padding: 8px 12px;
width: 260px;
}
.text {
width: 100%;
height: fit-content;
font-family: var(--wx-font-family);
color: #ffffff;
text-transform: capitalize;
font-size: 14px;
line-height: 20px;
}
.text + .text {
margin-top: 8px;
}
.text:last-child {
margin-bottom: 0;
}
.details {
word-wrap: break-word;
}
.caption {
font-weight: 600;
}
After the tooltip template is ready, we can import and apply it via the Tooltip content property.
Component.jsx
import TooltipContent from './TooltipContent';
import './TooltipContent.css';
// ...
<Tooltip api={api} content={TooltipContent}>
<Gantt
init={setApi}
tasks={data.tasks}
links={data.links}
scales={data.scales}
columns={columns}
/>
</Tooltip>
// ...
For more information about configuring tooltips, refer to the following documentation section: Adding tooltips.
Tuning Look and Feel
When initializing the project, we applied the WillowDark skin, which provides a clean and elegant design. However, to truly capture the essence of Halloween, we need to go beyond the basics and incorporate custom styles that resonate with the season's spirit. The key customizations include a dark background, purple task bars, orange summary tasks, and lime green dependency links for that perfect Halloween vibe.
To change the default theme settings, create a separate CSS file with custom CSS variables and apply it to the Halloween Gantt app.
This is how we replace colors in Component.css (excerpt):
.content {
--wx-background: #161616;
--wx-color-primary: #3c2369;
--wx-color-danger: #eb691f;
--wx-gantt-summary-color: #ee8c1d;
--wx-gantt-summary-fill-color: #eb691f;
--wx-gantt-task-color: #3c2369;
--wx-gantt-task-fill-color: #582c8f;
--wx-gantt-link-color: #9ee41f;
/* Other custom variables */
}
.content .wx-bar.wx-milestone .wx-content {
background: url('/assets/pumpkin.svg') no-repeat center / cover;
}
Apply the styles by wrapping your component in a container with the content class:
Demo.jsx
import { WillowDark } from '@svar-ui/react-gantt';
import Component from './components/Component';
import './Component.css';
function Demo() {
return (
<div className="demo">
<WillowDark />
<div className="content wx-willow-dark-theme">
<Component />
</div>
</div>
);
}
export default Demo;
You can find the complete list of rules in the related demo.
Summing Up
With these tweaks, our Halloween task manager is ready to bring order (and a little mischief) to the seasonal chaos.
We’ve customized tooltips, streamlined the context menu, added user assignments, and wrapped everything in a festive theme, all powered by SVAR React Gantt!
If you enjoy working with SVAR React Gantt, we’d really appreciate your ⭐ on GitHub. It helps us grow and keep improving the component.




Top comments (0)