Interactive Gantt Charts in Vanilla JavaScript – VikCraft Gantt

Category: Chart & Graph , Javascript | September 3, 2025
Authorntshvicky
Last UpdateSeptember 3, 2025
LicenseMIT
Views220 views
Interactive Gantt Charts in Vanilla JavaScript – VikCraft Gantt

VikCraft Gantt is a lightweight  Vanilla JavaScript library that creates interactive and highly configurable Gantt charts for project management applications.

It transforms your project data into dynamic, visual timelines where users can drag tasks, adjust durations, manage dependencies, and edit project details directly within the chart interface.

Features:

  • Interactive Task Management: Add, edit, update, and delete tasks directly from the chart interface with built-in modal dialogs.
  • Hierarchical Task Structure: Support for parent-child task relationships with visual indentation and expandable task groups.
  • Visual Dependency Management: Connecting lines between dependent tasks with automatic layout adjustment.
  • Drag and Resize Operations: Interactive adjustment of task dates and durations through direct manipulation.
  • Configurable Grid Columns: Customizable data columns with support for text, date, number, select, and multiselect field types.
  • Custom Modal Templates: Complete control over add/edit interfaces using your own HTML structure.
  • Multiple View Modes: Switch between Day, Week, and Month timeline views with zoom functionality.
  • Dynamic Column Resizing: User-adjustable column widths with persistent sizing.
  • Built-in Theme System: Light, dark, and narrow themes with programmatic theme switching.
  • Event Callback System: Hooks for onTaskAdd, onTaskUpdate, and onTaskDelete operations.

Use Cases

  • Internal Project Management Tools: For teams that need a custom project planning tool but don’t want the overhead or cost of a full-blown SaaS product.
  • Client-Facing Project Timelines: When you need to give clients a clear, visual representation of a project’s schedule and progress. It’s a professional touch that avoids sending static spreadsheets back and forth.
  • Resource Allocation Dashboards: Helps visualize who is working on what and when. This makes it easier to spot potential scheduling conflicts or identify team members who are over or under-utilized.
  • Software Release Planning: Ideal for mapping out development cycles, from research and design to backend and frontend development sprints, including all the dependencies between them.

How to use it:

1. Download the package and load the following JS/CSS files in the document.

<link rel="stylesheet" href="vikcraft-gantt-styles.css">
<script s"vikcraft-gantt-script.js"></script>

2. Create an empty DIV container to hold your Gantt chart.

<div id="myChart"></div>

3. Create a theme switcher that allows your user to switch between themes.

<div class="controls">
  <span>Change Theme:</span>
  <button id="theme-light">Light</button>
  <button id="theme-dark">Dark</button>
  <button id="theme-narrow">Narrow</button>
</div>

4. Define your task data as an array of objects. Each task object requires these properties:

  • id (number): Unique identifier for the task.
  • name (string): Display name for the task.
  • start (string): Start date in YYYY-MM-DD format.
  • end (string): End date in YYYY-MM-DD format.
  • progress (number): Completion percentage from 0 to 100.
  • parent (number | null): Parent task ID for hierarchical relationships, or null for top-level tasks.
  • assignedUser (Array): Array of resource IDs assigned to the task.
  • dependencies (Array): Array of task IDs that must complete before this task can start.
const myTasks = [
  { 
    id: 1, 
    name: 'Project Kick-off', 
    start: '2025-07-05', 
    end: '2025-07-06', 
    progress: 100, 
    assignedUser: ['alice'], 
    status: 'Completed' 
  },
  // more tasks here
];
const projectResources = [
  { id: 'alice', name: 'Alice' },
  { id: 'bob', name: 'Bob' },
  { id: 'charlie', name: 'Charlie' },
  { id: 'dana', name: 'Dana' },
  { id: 'eve', name: 'Eve' },
  { id: 'frank', name: 'Frank' },
  { id: 'grace', name: 'Grace' }
];

5. Instantiate VikCraftGantt with the ID of your container element, your task data, and a configuration object. Available options:

  • theme (string): Visual theme selection – ‘light’, ‘dark’, or ‘narrow’. Default is ‘light’.
  • resources (array): User list in format [{ id, name }]. Used for assignedUser multiselect columns.
  • columns (array): Column definitions for the data grid with properties for id, text, flex sizing, and input types.
  • features (object): UI feature toggles including zoom, viewModes, and columnResize capabilities.
  • onTaskAdd (function): Callback triggered after task creation with the new task object as parameter.
  • onTaskUpdate (function): Callback triggered after task modification with the updated task object.
  • onTaskDelete (function): Callback triggered after task deletion with the deleted task ID.
const ganttInstance = new VikCraftGantt('myChart', myTasks, {
  theme: 'light',
  resources: projectResources,
  columns: [
    { id: 'sl_no', text: '#', flex: '0 0 40px' },
    { id: 'name', text: 'Task Name', flex: '1 1 220px', type: 'text' },
    { id: 'start', text: 'Start Date', flex: '0 0 90px', type: 'date' },
    { id: 'end', text: 'End Date', flex: '0 0 90px', type: 'date' },
    { id: 'progress', text: 'Progress', flex: '0 0 90px', type: 'number' },
    { id: 'assignedUser', text: 'Assignees', flex: '1 1 150px', type: 'multiselect', optionsSource: 'resources' },
    { id: 'status', text: 'Status', flex: '0 0 100px', type: 'select', options: ['Not Started', 'In Progress', 'Completed', 'On Hold'] } // Example of a custom dropdown column
  ],
  features: { zoom: true, viewModes: true, columnResize: true },
  onTaskAdd: (task) => {
    console.log("API POST call for:", task);
  },
  onTaskUpdate: (task) => {
    console.log("API PUT call for:", task);
  },
  onTaskDelete: (taskId) => {
    console.log("API DELETE call for:", taskId);
  }
});

6. While the default modal is quite capable, you’ll often need a more tailored experience. This is where the modalTemplate option comes in. It lets you supply a complete HTML string for the modal.

The key requirement here is to follow a specific ID convention for your form inputs: modal-task-YOUR_COLUMN_ID. For example, a field for name must have id="modal-task-name". This allows the library to automatically wire up your custom HTML with its internal data handling logic.

Here’s a condensed example of a custom template string:

const ganttInstance = new VikCraftGantt('myChart', myTasks, {
  modalTemplate: `
    <div class="gantt-modal">
      <h2 id="modal-title">Edit Task</h2>
      <div class="gantt-modal-content" id="custom-modal-content">
        <form onsubmit="return false;">
          <input type="hidden" id="modal-task-id" />
          <div class="form-group">
            <label for="modal-task-name">Task Name (Custom)</label>
            <input type="text" id="modal-task-name" required>
          </div>
          <div class="form-group">
            <label for="modal-task-parent">Parent Task (Custom)</label>
            <select id="modal-task-parent"></select>
          </div>
          <div class="form-group">
            <label for="modal-task-start">Start Date (Custom)</label>
            <input type="date" id="modal-task-start" required>
          </div>
          <div class="form-group">
            <label for="modal-task-end">End Date (Custom)</label>
            <input type="date" id="modal-task-end" required>
          </div>
          <div class="form-group">
            <label for="modal-task-progress">Progress (%) (Custom)</label>
            <input type="number" id="modal-task-progress" min="0" max="100">
          </div>
          <div class="form-group">
            <label for="modal-task-assignedUser">Assigned To (Custom)</label>
            <select id="modal-task-assignedUser" multiple></select>
          </div>
          <div class="form-group">
            <label for="modal-task-status">Task Status (Custom)</label>
            <select id="modal-task-status"></select>
          </div>
          <div class="form-group">
            <label for="modal-task-dependencies">Dependencies (Custom)</label>
            <select id="modal-task-dependencies" multiple></select>
          </div>
        </form>
      </div>
      <div class="gantt-modal-actions">
        <button class="btn-delete" id="modal-delete-btn">Delete</button>
        <button class="btn-cancel" id="modal-cancel-btn">Cancel</button>
        <button class="btn-save" id="modal-save-btn">Save</button>
      </div>
    </div>`
});

When you provide this template, the library finds elements like #modal-task-name and #modal-task-status and handles populating them with task data and saving the values back.

7. You can also fetch tasks and resources from an externail resource.

async function initializeGanttWithApiData() {
  // Define your API endpoints
  const tasksApiEndpoint = 'http://localhost:8000/projects/46/tasks/json/';
  const resourcesApiEndpoint = 'http://8000/projects/46/team/options/'; // Corrected port if needed
  try {
    // Fetch both tasks and resources
    const [taskResponse, resourceResponse] = await Promise.all([
      fetch(tasksApiEndpoint),
      fetch(resourcesApiEndpoint)
    ]);
    if (!taskResponse.ok) throw new Error(`Failed to fetch tasks: ${taskResponse.statusText}`);
    if (!resourceResponse.ok) throw new Error(`Failed to fetch resources: ${resourceResponse.statusText}`);
    const rawTasks = await taskResponse.json();
    const projectResources = await resourceResponse.json();
    // MAP YOUR API DATA
    const mappedTasks = rawTasks.map(task => ({
      id: task.id,
      name: task.name,
      start: task.start,
      end: task.end,
      progress: task.percent,
      assignedUser: task.resources, // Assuming 'resources' is an array of resource IDs/names
      dependencies: task.depend_ids,
      parent: task.parent,
      status: task.status // Assuming 'status' comes from your API
    }));
    const ganttOptions = {
      theme: 'light',
      resources: projectResources,
      columns: [{
          id: 'sl_no',
          text: '#',
          flex: '0 0 40px'
        },
        {
          id: 'name',
          text: 'Task Name',
          flex: '1 1 220px',
          type: 'text'
        },
        {
          id: 'start',
          text: 'Start Date',
          flex: '0 0 90px',
          type: 'date'
        },
        {
          id: 'end',
          text: 'End Date',
          flex: '0 0 90px',
          type: 'date'
        },
        {
          id: 'progress',
          text: 'Progress',
          flex: '0 0 90px',
          type: 'number'
        },
        {
          id: 'assignedUser',
          text: 'Assignees',
          flex: '1 1 150px',
          type: 'multiselect',
          optionsSource: 'resources'
        },
        {
          id: 'status',
          text: 'Status',
          flex: '0 0 100px',
          type: 'select',
          options: ['Not Started', 'In Progress', 'Completed', 'On Hold']
        }
      ],
      features: {
        zoom: true,
        viewModes: true,
        columnResize: true
      },
      onTaskAdd: (task) => {
        console.log("Task Added:", task);
        // Place your API POST call here
      },
      onTaskUpdate: (task) => {
        console.log("Task Updated:", task);
        // Place your API PUT/PATCH call here
      },
      onTaskDelete: (taskId) => {
        console.log("Task Deleted:", taskId);
        // Place your API DELETE call here
      }
    };
    ganttInstance = new VikCraftGantt('my-gantt-chart', mappedTasks, ganttOptions);
  } catch (error) {
    console.error("Failed to initialize Gantt chart:", error);
    document.getElementById('my-gantt-chart').innerHTML = `

8. Change themes dynamically using the setTheme method:

// Switch to dark theme
ganttInstance.setTheme('dark');
// Switch to narrow theme (compact view)
ganttInstance.setTheme('narrow');
// Return to light theme
ganttInstance.setTheme('light');

FAQs:

Q: How do I integrate VikCraft Gantt with a REST API for persistent data storage?
A: Use the callback functions (onTaskAdd, onTaskUpdate, onTaskDelete) to make HTTP requests to your API endpoints. The library provides the complete task object or task ID in these callbacks, making it straightforward to sync changes with your backend. For initial data loading, fetch your tasks from the API and map them to the required format before initializing the Gantt instance.

Q: Can I customize the appearance of individual task bars based on task properties?
A: While the library provides three built-in themes, individual task bar styling requires CSS customization. You can target specific tasks using CSS attribute selectors based on data attributes that the library adds to task elements. For more advanced styling, consider extending the library or using CSS-in-JS solutions to dynamically apply styles based on task data.

Q: Can I add custom columns to the grid that are not part of the default task structure?
A: Yes. Just add the custom field (e.g., status: 'In Progress') to your task objects in the data array. Then, define that field in the columns configuration array. You can specify its type as 'text', 'number', or 'select' to have it render correctly in the default edit modal.

Q: How do I handle large datasets with hundreds or thousands of tasks?
A: VikCraft Gantt renders all tasks in the DOM simultaneously, which can impact performance with large datasets. For projects with more than 500 tasks, consider implementing virtual scrolling or pagination at the application level. Filter tasks by date range or project phase to reduce the number of rendered elements while maintaining usability.

Q: My custom modal isn’t populating or saving data correctly. What did I do wrong?
A: The most common issue is the ID naming convention. Double-check that every input, select, and textarea element in your modalTemplate that corresponds to a task data field has an ID in the format modal-task-COLUMN_ID. For example, progress needs id="modal-task-progress". Also, ensure your save, cancel, and delete buttons have the required IDs: modal-save-btn, modal-cancel-btn, and modal-delete-btn.

Q: How can I style the chart beyond the built-in themes?
A: Since you include the vikcraft-gantt-styles.css file yourself, you can override its styles with your own CSS. Use your browser’s developer tools to inspect the chart elements, identify the classes you want to change (e.g., .gantt-task-bar, .gantt-grid-header), and write more specific CSS rules in your own stylesheet to customize the appearance.

Related Resources:

You Might Be Interested In:


Leave a Reply