Project dashboards often need more than tables and cards. When I build project management interfaces, a Gantt Chart is one of the most useful ways to show tasks, dates, progress, and project timelines in one place.
A React JS Gantt Chart component can look complex at first, but the basic idea is straightforward. You store project tasks in an array, calculate each task’s position on a timeline, and render a visual bar for every task.
Let’s build a reusable React JS Gantt Chart component using a simple project example for a software team in Austin, Texas.
What Is a React JS Gantt Chart Component?
A Gantt Chart displays project tasks across a timeline. Each task appears as a horizontal bar that starts on its scheduled date and ends on its completion date.
For example, imagine a website project managed by John Miller in Austin, Texas:
- Project Planning: September 1–3
- UI Design: September 4–8
- React Development: September 9–15
- Testing: September 16–18
A React component is a reusable JavaScript function that returns UI. We can create a reusable Gantt Chart component and pass project tasks to it using props.
If you are new to React components, you may also find this guide on React props useful.
Create a React Application
You need a React project before creating the component. If you have not created one yet, follow this guide on how to set up a ReactJS environment and create your first React app.
For this example, our project structure looks like this:
src/
│
├── App.jsx
├── GanttChart.jsx
└── App.cssWe will create:
- A project task data array.
- A reusable Gantt Chart component.
- Timeline date headers.
- Task bars with dynamic positions.
- Progress indicators.
Build the React JS Gantt Chart Component
Step 1: Create Project Task Data
First, create the task data inside App.jsx.
App.jsx
import GanttChart from "./GanttChart";
import "./App.css";
function App() {
const tasks = [
{
id: 1,
name: "Project Planning",
owner: "John Miller",
startDay: 0,
duration: 3,
progress: 100
},
{
id: 2,
name: "UI Design",
owner: "Emily Carter",
startDay: 3,
duration: 5,
progress: 80
},
{
id: 3,
name: "React Development",
owner: "Michael Brown",
startDay: 8,
duration: 7,
progress: 50
},
{
id: 4,
name: "Testing",
owner: "Sarah Johnson",
startDay: 15,
duration: 3,
progress: 20
}
];
return (
<div className="app">
<h1>Website Project Timeline</h1>
<GanttChart tasks={tasks} />
</div>
);
}
export default App;Here, each task contains the information our Gantt Chart needs.
The startDay value controls where the task starts on the timeline. The duration value controls how wide the task bar becomes.
We pass the tasks to the Gantt Chart using props. Props allow a parent component to send data to another component.
Sample Output
The browser displays:
Website Project Timeline
Project Planning John Miller
UI Design Emily Carter
React Development Michael Brown
Testing Sarah JohnsonThe Gantt Chart component will convert these tasks into visual timeline bars.
Step 2: Create the GanttChart Component
Now create a file named GanttChart.jsx.
GanttChart.jsx
import React from "react";
function GanttChart({ tasks }) {
const totalDays = 18;
const dates = Array.from(
{ length: totalDays },
(_, index) => `Day ${index + 1}`
);
return (
<div className="gantt-container">
<div className="gantt-header">
<div className="task-column">Task</div>
<div className="timeline-header">
{dates.map((date) => (
<div key={date} className="date-cell">
{date}
</div>
))}
</div>
</div>
{tasks.map((task) => (
<div className="gantt-row" key={task.id}>
<div className="task-info">
<strong>{task.name}</strong>
<span>{task.owner}</span>
</div>
<div className="timeline-row">
<div
className="task-bar"
style={{
left: `${(task.startDay / totalDays) * 100}%`,
width: `${(task.duration / totalDays) * 100}%`
}}
>
<span>{task.progress}%</span>
</div>
</div>
</div>
))}
</div>
);
}
export default GanttChart;The important part of this component is the calculation below:
left: `${(task.startDay / totalDays) * 100}%`This calculation determines where the task starts.
For example, if a task starts on day 9 of an 18-day project:
9 / 18 × 100 = 50%The task starts halfway across the timeline.
The width calculation works similarly:
width: `${(task.duration / totalDays) * 100}%`This approach makes the component dynamic. You can change the task data without manually changing the CSS position of every bar.
Step 3: Add CSS Styling
Now create the styling for the Gantt Chart.
App.css
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f5f7fb;
}
.app {
max-width: 1200px;
margin: 40px auto;
padding: 20px;
}
.app h1 {
margin-bottom: 30px;
}
.gantt-container {
background: white;
border-radius: 8px;
overflow-x: auto;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
}
.gantt-header {
display: flex;
min-width: 1000px;
border-bottom: 1px solid #ddd;
}
.task-column {
width: 250px;
min-width: 250px;
padding: 15px;
font-weight: bold;
border-right: 1px solid #ddd;
}
.timeline-header {
flex: 1;
display: grid;
grid-template-columns: repeat(18, 1fr);
}
.date-cell {
padding: 15px 5px;
text-align: center;
font-size: 12px;
border-right: 1px solid #eee;
}
.gantt-row {
display: flex;
min-width: 1000px;
min-height: 70px;
border-bottom: 1px solid #eee;
}
.task-info {
width: 250px;
min-width: 250px;
padding: 15px;
border-right: 1px solid #ddd;
}
.task-info strong {
display: block;
}
.task-info span {
display: block;
margin-top: 5px;
font-size: 13px;
color: #666;
}
.timeline-row {
flex: 1;
position: relative;
background-image: linear-gradient(
to right,
#eee 1px,
transparent 1px
);
background-size: calc(100% / 18) 100%;
}
.task-bar {
position: absolute;
top: 20px;
height: 30px;
border-radius: 4px;
padding: 7px;
font-size: 12px;
color: white;
background: #2563eb;
overflow: hidden;
}
.task-bar span {
white-space: nowrap;
}The .timeline-row uses position: relative. This is important because the task bars use position: absolute.
Each task bar calculates its own left and width values dynamically.
Sample Output
The browser now displays a timeline similar to this:
Task Day 1 Day 2 Day 3 Day 4 ...
Project Planning █████████
John Miller
UI Design ███████████████
Emily Carter
React Development ████████████████████
Michael Brown
Testing ███████
Sarah JohnsonI executed the above example code and added the screenshot below.

Add Progress Inside the Gantt Chart
A real-world Gantt Chart should also show project progress.
We already store a progress value for each task. Let’s add a progress overlay inside the task bar.
Update GanttChart.jsx.
GanttChart.jsx
import React from "react";
function GanttChart({ tasks }) {
const totalDays = 18;
const dates = Array.from(
{ length: totalDays },
(_, index) => `Day ${index + 1}`
);
return (
<div className="gantt-container">
<div className="gantt-header">
<div className="task-column">Task</div>
<div className="timeline-header">
{dates.map((date) => (
<div key={date} className="date-cell">
{date}
</div>
))}
</div>
</div>
{tasks.map((task) => (
<div className="gantt-row" key={task.id}>
<div className="task-info">
<strong>{task.name}</strong>
<span>{task.owner}</span>
</div>
<div className="timeline-row">
<div
className="task-bar"
style={{
left: `${(task.startDay / totalDays) * 100}%`,
width: `${(task.duration / totalDays) * 100}%`
}}
>
<div
className="task-progress"
style={{
width: `${task.progress}%`
}}
/>
<span>{task.progress}% Complete</span>
</div>
</div>
</div>
))}
</div>
);
}
export default GanttChart;Add the following CSS:
.task-bar {
position: absolute;
top: 20px;
height: 32px;
border-radius: 4px;
background: #bfdbfe;
overflow: hidden;
color: #111827;
}
.task-progress {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: #2563eb;
}
.task-bar span {
position: relative;
z-index: 1;
display: block;
padding: 8px;
font-size: 12px;
white-space: nowrap;
}Now each task shows both its total scheduled duration and its completion percentage.
Sample Output
Project Planning ███████████████████ 100% Complete
UI Design ███████████████░░░░ 80% Complete
React Development ██████████░░░░░░░░░ 50% Complete
Testing ████░░░░░░░░░░░░░░░ 20% CompleteMake the React JS Gantt Chart Interactive
Next, let’s allow users to select a task.
For this, we need state. State stores data that can change while a user interacts with a component.
We will use the useState hook. A hook is a React function that adds features such as state management to a functional component.
If you are also learning React events, this guide on handling events in React JS is useful.
Update App.jsx.
import { useState } from "react";
import GanttChart from "./GanttChart";
import "./App.css";
function App() {
const [selectedTask, setSelectedTask] = useState(null);
const tasks = [
{
id: 1,
name: "Project Planning",
owner: "John Miller",
startDay: 0,
duration: 3,
progress: 100
},
{
id: 2,
name: "UI Design",
owner: "Emily Carter",
startDay: 3,
duration: 5,
progress: 80
},
{
id: 3,
name: "React Development",
owner: "Michael Brown",
startDay: 8,
duration: 7,
progress: 50
},
{
id: 4,
name: "Testing",
owner: "Sarah Johnson",
startDay: 15,
duration: 3,
progress: 20
}
];
return (
<div className="app">
<h1>Website Project Timeline</h1>
<GanttChart
tasks={tasks}
selectedTask={selectedTask}
setSelectedTask={setSelectedTask}
/>
{selectedTask && (
<div className="task-details">
<h2>{selectedTask.name}</h2>
<p>
Owner: {selectedTask.owner}
</p>
<p>
Progress: {selectedTask.progress}%
</p>
</div>
)}
</div>
);
}
export default App;Now update GanttChart.jsx.
import React from "react";
function GanttChart({
tasks,
selectedTask,
setSelectedTask
}) {
const totalDays = 18;
const dates = Array.from(
{ length: totalDays },
(_, index) => `Day ${index + 1}`
);
return (
<div className="gantt-container">
<div className="gantt-header">
<div className="task-column">Task</div>
<div className="timeline-header">
{dates.map((date) => (
<div key={date} className="date-cell">
{date}
</div>
))}
</div>
</div>
{tasks.map((task) => (
<div className="gantt-row" key={task.id}>
<div className="task-info">
<strong>{task.name}</strong>
<span>{task.owner}</span>
</div>
<div className="timeline-row">
<button
type="button"
className={`task-bar ${
selectedTask?.id === task.id
? "selected-task"
: ""
}`}
onClick={() => setSelectedTask(task)}
style={{
left: `${(task.startDay / totalDays) * 100}%`,
width: `${(task.duration / totalDays) * 100}%`
}}
>
<div
className="task-progress"
style={{
width: `${task.progress}%`
}}
/>
<span>{task.progress}% Complete</span>
</button>
</div>
</div>
))}
</div>
);
}
export default GanttChart;Add these styles:
.task-bar {
border: none;
cursor: pointer;
text-align: left;
}
.selected-task {
outline: 3px solid #111827;
}
.task-details {
margin-top: 25px;
padding: 20px;
background: white;
border-radius: 8px;
}An event handler is a function that runs after a user action. Here, onClick runs when someone clicks a task bar.
Sample Output
Before clicking a task:
Website Project Timeline
Project Planning
UI Design
React Development
TestingAfter clicking React Development:
React Development
Owner: Michael Brown
Progress: 50%The selected task also receives a visible outline.
Pro Tip: I usually keep the task data separate from the visual calculations. When dates, durations, and progress values change, React can update the chart without forcing me to manually reposition every task bar.
Why Build a Custom React Gantt Chart?
A custom component gives you control over the UI and behavior.
You can extend this example to support:
- Real calendar dates
- Drag-and-drop scheduling
- Task dependencies
- Multiple project teams
- API data
- Filters
- Zoom levels
- Monthly and weekly views
- Task editing
- Progress updates
For larger applications, you can load task data from an API and update the chart when the data changes.
You can also use the same component design principles when building forms. For example, see how to reset a form in React JS.
Things to Keep in Mind
- Keep task data separate: Store task information in objects and calculate timeline positions dynamically instead of hardcoding CSS values.
- Use stable keys: Always use a unique task ID when performing list rendering with
map(). - Avoid direct mutation: Create a new array or object when updating project tasks. An immutable update helps React detect changes correctly.
- Consider accessibility: Use buttons for clickable task bars and provide clear text so keyboard and screen-reader users can interact with the chart.
- Handle large timelines carefully: Hundreds of tasks can affect rendering performance. Add virtualization or pagination only when your application actually needs it.
- Use real dates for production apps: The
startDayapproach works well for learning, but production Gantt Charts should calculate positions from actual JavaScript dates.
Frequently Asked Questions
How do I create a Gantt Chart in React JS?
Create an array of task objects containing task names, start positions, durations, and progress values. Then use map() to render each task as an absolutely positioned bar inside a timeline component.
Can I build a React Gantt Chart without a library?
Yes. You can build a custom Gantt Chart with React, JavaScript, and CSS. This approach works especially well when you need complete control over the layout and features.
How do I position task bars dynamically in a React Gantt Chart?
Calculate the left position and width as percentages of the total timeline. This allows the component to position tasks dynamically based on their start date and duration.
How do I add task progress to a Gantt Chart?
Store a progress percentage in each task object and render an inner progress element. Set its width dynamically using the task’s progress value.
Can I make a React Gantt Chart interactive?
Yes. Use useState to store the selected task, filters, or editing state. An onClick event handler can update the selected task when users click a timeline bar.
How should I load Gantt Chart data from an API?
Store the API response in React state and render the task array after the request succeeds. You should also handle loading and error states before displaying the project timeline.
A React JS Gantt Chart component becomes much easier to build when you break it into task data, timeline calculations, reusable components, and CSS positioning. Start with a simple timeline like this, then add real dates, API integration, filters, and advanced project management features as your application grows.
You May Also Like
- How to Set Up ReactJS and Create Your First React App
- Understanding Props in React JS
- How to Handle Events in React JS
- How to Reset a Form in React JS
- TypeScript vs React

Bijay Kumar is an experienced Python and AI professional who enjoys helping developers learn modern technologies through practical tutorials and examples. His expertise includes Python development, Machine Learning, Artificial Intelligence, automation, and data analysis using libraries like Pandas, NumPy, TensorFlow, Matplotlib, SciPy, and Scikit-Learn. At PythonGuides.com, he shares in-depth guides designed for both beginners and experienced developers. More about us.