Bryntum
22 January 2025

How to connect and sync Bryntum Gantt to monday.com

With Bryntum Gantt, you can build super fast and fully customizable Gantt charts with pure JavaScript. You can easily use […]

With Bryntum Gantt, you can build super fast and fully customizable Gantt charts with pure JavaScript. You can easily use Bryntum Gantt with React, Vue, or Angular.

In this tutorial, we’ll connect and sync Bryntum Gantt to monday.com by:

Getting started

Clone the starter GitHub repository. This starter repository uses the development server and JavaScript bundler, Vite. You’ll need Node.js version 14.18+ for Vite to work.

Install the Vite dev dependency by running the following command:

npm install

The dev server is configured to run on http://localhost:8080/ in the vite.config.js file.

Run the local dev server using npm run dev and you’ll see a blank page.

Create a Gantt chart using Bryntum Gantt

We’ll install the Bryntum Gantt component using npm.

Use the guide to using the Bryntum npm repository to log in to the Bryntum component registry.

Then, install Bryntum Gantt by following step four of the guide to getting started with Bryntum Gantt.

Let’s import the Bryntum Gantt component and give it some basic configuration. In the main.js file, add the following lines:

import { Gantt } from '@bryntum/gantt/gantt.module.js';
const gantt = new Gantt({
    appendTo: document.body,
    startDate: '2025-01-01',
    endDate: new Date(new Date().getFullYear() + 1, new Date().getMonth(), new Date().getDate()),
    project: {
        tasksData: [
            {
                id: 1,
                name: 'Write docs',
                expanded: true,
                children: [
                    {
                        id: 2,
                        name: 'Proof-read docs',
                        startDate: '2025-01-02',
                        endDate: '2025-01-09',
                        manuallyScheduled: true
                    },
                    {
                        id: 3,
                        name: 'Release docs',
                        startDate: '2025-01-09',
                        endDate: '2025-01-10',
                        manuallyScheduled: true
                    }
                ]
            }
        ],
    },
    columns: [
        { type: 'name', width: 200 }
    ]
});

Here, we import the component, create a new Bryntum Gantt instance, and pass a configuration object into it. We add the Gantt chart to the <body> of the DOM.

You can set up your Gantt chart so that it displays specific dates on opening. In this example, we set the startDate to the current date and the endDate to one year from today, but you can change these dates to the duration of your project.

For simplicity, we populate TaskData by passing in data inline. We have a parent item containing two children items. You can learn more about working with data in the Bryntum docs.

If you run your dev server now, you’ll see the parent and children items in the Bryntum Gantt chart:

The Gantt chart uses the Stockholm theme, which is one of five available themes for Bryntum Gantt charts. There is a link to the CSS for the Stockholm theme in index.html. If you’d like to create a custom theme, read more about styling your Gantt chart in the Bryntum customization docs.

Now, let’s learn how to use the monday.com GraphQL API to retrieve a list of items from a monday.com user account.

Set up monday.com

Sign up or log in to monday.com. If you’re setting up a new personal account, click the Continue button when asked what you want to manage first:

Click Continue again when asked what you want to focus on first:

Name your board Bryntum Monday Integration:

Select the Owner, Status, and Timeline columns for your board. These can also be added later.

Select the Tasks overview, Tasks by status, and Tasks by owner widgets for your dashboard:

Add the Gantt view layout:

Click the Get started button in the List your projects step:

You’ll see your Bryntum Monday Integration board and three example tasks displayed on the monday.com work management dashboard:

Delete the Due date column by hovering over the header name, clicking the ellipsis icon, and selecting the Delete menu item. Delete the Dependent On column too, as we won’t work with task dependencies in this guide.

Rename the Timeline column Dates.

Add a Subitem called Task 3 subtask 1 to Task 3. Then, rename the Date column Dates and change it to a timeline column by hovering over the column name, Dates, clicking the ellipsis icon, selecting Change column type from the menu, and choosing Timeline. This creates a new column. Delete the old Dates column and set the dates for the subtask in the new column.

Get your access token

You need an access token to access the monday.com board items for your Bryntum Gantt component. Click on your profile picture in the top-right corner of the screen and select Developers in the popup menu:

This opens the monday.com Developer Center. Open the My access tokens page from the left-hand navigation panel. Click Show to reveal and copy your access token.

Save the access token to use later.

Using the monday.com GraphQL API playground

The monday.com API is built with GraphQL and has a GraphQL playground for testing and building your queries. To access it, open the API playground page from the Developer Center navigation panel.

You can edit the GraphQL query and then execute it by clicking the “Execute query” button represented by the blue play icon.

Use the monday.com GraphQL API to access your monday.com items

Now that you have an access token, we can use it to query the monday.com API and retrieve all the items from our boards.

Create a .env file in the root folder of your project and add your access token to the following environment variable:

VITE_MONDAY_ACCESS_TOKEN=

For simplicity, we’ll use the Fetch API on the client side with this access token.

Alternatively, you can learn how to protect your access token in an Express.js server in the guide to connecting Bryntum Scheduler Pro to monday.com, which uses the monday.com SDK rather than the Fetch API to import data to a Bryntum component.

In the graph.js file, add the code below:

import { createGantt, updateChildrenList, updateParentList, accessToken } from "./main.js";
function getTasksFromMonday() {
    const query = `{
        boards(limit: 2) {
        id
        items_page {
          items {
            id
            name
            parent_item{id}
            column_values {
              id
              text
              value
              column {
                title
                settings_str
              }
            }
          }
        }
      }
    }`;
    fetch("https://api.monday.com/v2", {
        method: 'post',
        headers: {
            'Content-Type': 'application/json',
            'Authorization' : accessToken,
            'API-Version' : '2024-10'
        },
        body: JSON.stringify({
            'query': query
        })
    })
        .then((result) => result.json())
        .then(res => {
            let eventList = [];
            eventList = updateParentList(res, eventList);
            eventList = updateChildrenList(res, eventList);
            createGantt(eventList);
        });
}
export { getTasksFromMonday };

In this code, we define a getTasksFromMonday function, which makes a query to the monday.com GraphQL API. The query is limited to two boards and returns the items from each board. We query the name, ID, parent item ID, and column values of each item. We’ll use these values to define the items added to the Bryntum Gantt.

The query then initiates a few functions: The updateParentList function adds the parent-level items to a list called eventList, the updateChildrenList function adds the subitems to their appropriate parent items and returns the updated eventList, and the createGantt function uses the data from the eventList to create the Gantt instance and TaskData.

Replace all the code in the main.js file with the following code:

import { Gantt } from '@bryntum/gantt/gantt.module.js';
import { getTasksFromMonday } from './graph.js';
const accessToken = import.meta.env.VITE_MONDAY_ACCESS_TOKEN
let isAppended = false;
getTasksFromMonday();
function createGantt(eventList) {
    let column_id_list_child;
    for (let i = 0; i < eventList.length; i++) {
        if (eventList[i]?.children.length > 0) {
          column_id_list_child = eventList[i].children[0].column_values[2].id;
          break;
        }
    }
    const column_id_list = {child: column_id_list_child, parent: eventList[0].column_values[3].id};
    const parent_board_id = eventList[0].board_id;
    const gantt = new Gantt({
        // startDate is today and endDate is 1 year from today
        startDate: new Date(),
        endDate: new Date(new Date().getFullYear() + 1, new Date().getMonth(), new Date().getDate()),
        project : {
            tasksData : eventList,
        },
        column_ids: column_id_list,
        board_id: parent_board_id, 
        columns : [
            { type : 'name', width : 200 }
        ]
    });
    if(isAppended == false){
        gantt.appendTo = document.body;
        isAppended = true;
    }
}
export { createGantt, updateParentList, updateChildrenList, accessToken };

The above createGantt function creates our Gantt instance. In it, we use the isAppended variable to ensure the Gantt isn’t appended twice when the Vite page reloads and use the column_id_list_child variable to save the ID of the column containing subitems. This only works if there is a subitem in your Gantt. If not, you need to manually add this value.

If you run the GraphQL query used in the getTasksFromMonday function in the monday.com GraphQL playground, you can see the column_value IDs. The ID for the Dates column containing subitems has a random string in it: "id": "dates_mkm2h7bj",.

Now let’s add the updateParentList to our code. Insert the following below the createGantt function:

function updateParentList(results, eventList) {
    let parent_board = results.data.boards[1];
    for (let j = 0; j < parent_board.items_page.items.length; j++) {
        eventList.push({
            id: parent_board.items_page.items[j].id,
            monday_id : parent_board.items_page.items[j].id,
            name     : parent_board.items_page.items[j].name,
            startDate : parent_board.items_page.items[j].column_values[3].text.slice(0,10),
            endDate : parent_board.items_page.items[j].column_values[3].text.slice(13,23),
            expanded : false,
            children : [],
            board_id: parent_board.id,
            column_values: parent_board.items_page.items[j].column_values,
            manuallyScheduled: true
        });
    }
    return eventList;
}

Here, the updateParentList function loops through each of the boards returned from the getTasksFromMonday query. Each item from the parent_board is added to the eventList.

Now let’s add all subitems to their parent items with the updateChildrenList function. Add these lines below the updateParentList function:

function updateChildrenList(results, eventList) {
    let child_board = results.data.boards[0];
    for (let j = 0; j < child_board.items_page.items.length; j++) {
        if(child_board.items_page.items[j].parent_item != null){
            const parent_id = child_board.items_page.items[j].parent_item.id;
            const child_id = child_board.items_page.items[j].id;
            for (let k = 0; k < eventList.length; k++) {
                if (parent_id == eventList[k].id ) {
                    eventList[k].children.push({
                        id : child_id,
                        monday_id : child_id,
                        name : child_board.items_page.items[j].name,
                        startDate : child_board.items_page.items[j].column_values[2].text.slice(0,10),
                        endDate : child_board.items_page.items[j].column_values[2].text.slice(13,23),
                        board_id: child_board.id,
                        column_values: child_board.items_page.items[j].column_values,
                        manuallyScheduled: true
                    });
                    eventList[k].expanded = true;
                    eventList[k].endDate = child_board.items_page.items[j].column_values[2].text.slice(13,23);
                }
            }
        }
    }
    return eventList;
}

This function loops through the subitems and adds them to the appropriate parent item’s children list. These events are then passed to the createGantt function, which adds these items to the Gantt chart and appends the chart to the body of the DOM.

If you run the server now, you will see the items from your monday.com boards reflected in the Bryntum Gantt chart.

Implementing CRUD

Now that the Gantt chart has received all the items from monday.com, we’ll implement the rest of the CRUD functionality by taking advantage of the monday.com API. Each of the CRUD functions requires your access token.

Create events

In the graph.js file, add the following lines:

function addParentTaskToMonday(event, parent_name, parent_start, parent_end) {
    // add a parent task to monday.com with the given parameters
    let column_values = `{\"${event.source.column_ids.parent}\" : {\"from\" : \"${parent_start}\", \"to\": \"${parent_end}\"}}`;
    column_values = JSON.stringify(column_values);

    let query = `mutation{ create_item (board_id: ${event.source.board_id}, item_name: \"${parent_name}\", create_labels_if_missing: true, column_values: ${column_values}){ id board{id} column_values{id type text } }}`;

    const response = fetch ("https://api.monday.com/v2", {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        'Authorization' : accessToken,
        'API-Version' : '2024-10'
      },
      body: JSON.stringify({
        'query' : query
      })
    })
    .then(result => {
    let res = result.json();
    return res;
    })
    .then(res => {  
      event.records[0].data.monday_id = res.data.create_item.id;
      event.records[0].data.board_id = res.data.create_item.board.id;
      event.records[0].data.column_values = res.data.create_item.column_values;
      event.records[0].data.manuallyScheduled = true;
    })
}

Here, we define the addParentTaskToMonday function, which takes in data from a parent item, uses this data to query the monday.com API, and creates a parent-level item. The event represents an object passed from a listener, which we will set up later. The event is used here to update the Gantt item with the details from the query result of the newly created parent-level item.

Now, we will define the function that creates the subitems. In the graph.js file, add the following lines of code:

function addTaskToMonday(event, parent_id, child_name, child_start, child_end) {
    // add a task to monday.com with the given parameters
    let column_values = `{\"${event.source.column_ids.child}\" : {\"from\" : \"${child_start}\", \"to\": \"${child_end}\"}}`;
    column_values = JSON.stringify(column_values);
    let query = `mutation{ create_subitem (parent_item_id: ${parent_id}, item_name: \"${child_name}\", create_labels_if_missing: true, column_values: ${column_values}){ id board{id} parent_item{id} column_values{ id type text } }}`;
    const response = fetch ("https://api.monday.com/v2", {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        'Authorization' : accessToken,
        'API-Version' : '2024-10'
      },
      body: JSON.stringify({
        'query' : query
      })
    })
    .then(result => {
    let res = result.json();
    return res;
    })
    .then(res => {
        event.records[0].data.monday_id = res.data.create_subitem.id;
        event.records[0].data.board_id = res.data.create_subitem.board.id;
        event.records[0].data.parentId = res.data.create_subitem.parent_item.id;
        event.records[0].data.column_values = res.data.create_subitem.column_values;
        event.records[0].data.manuallyScheduled = true; 
        }
    );
}

Similar to the addParentTaskToMonday function, the addTaskToMonday function takes in data from the appropriate Gantt chart item, uses the data to query the monday.com API, and creates a subitem. The event is used to update the Gantt item with the details from the query result of the newly created item.

Update events

In the graph.js file, add the following function:

function updateTaskOnMonday(board_id, column_id, child_id, child_name, child_start, child_end, updateType) {
    if(updateType == "timeline") {
      var column_values = `{\"${column_id}\" : {\"from\" : \"${child_start}\", \"to\" : \"${child_end}\"}}`
    } else if (updateType == "name") {
      var column_values = `{\"name\" : \"${child_name}\"}`
    }
    column_values = JSON.stringify(column_values);
    let query = `mutation{ change_multiple_column_values (board_id: ${board_id}, item_id: ${child_id}, column_values: ${column_values}){ id }}`;
    const response = fetch ("https://api.monday.com/v2", {
      method: 'post',
      headers: {
        'Content-Type': 'application/json',
        'Authorization' : accessToken,
        'API-Version' : '2024-10'
      },
      body: JSON.stringify({
        'query' : query
      })
    })
    .then(result => {
      let res = result.json();
      return res;
    })
}

The updateTaskOnMonday function uses the updateType variable to determine which monday.com columns to query for the update. Either the Date column or the Name column will be updated and the appropriate query will be constructed.

Delete events

In the graph.js file, add the following function:

function deleteTask(id) {
  // delete a task on monday.com with the given parameters
  let query = `mutation{ delete_item (item_id: ${id}){ id }}`;
  const response = fetch ("https://api.monday.com/v2", {
    method: 'post',
    headers: {
      'Content-Type': 'application/json',
      'Authorization' : accessToken,
      'API-Version' : '2024-10'
    },
    body: JSON.stringify({
      'query' : query
    })
  })
}

The deleteTask function identifies the appropriate item by id and deletes the item.

Now, replace the exports at the bottom of graph.js with the following line:

export { getTasksFromMonday, addTaskToMonday, addParentTaskToMonday, updateTaskOnMonday, deleteTask };

Listening for event data changes in Bryntum Gantt

Next, we’ll add a data change event listener to the Bryntum Gantt, so that we can update the monday.com board when data in the Bryntum Gantt changes.

Add the following listeners config to the gantt instance in the main.js file:

    listeners: {
        dataChange: function (event) {
            updateMonday(event);
        }
    },

Here, we set a listener on our Bryntum Gantt to listen for any changes to the Gantt’s data store and to fire an "add" event when a Gantt item is created, an "update" event when an item is updated, and a "remove" event when an item is deleted.

The item retrieved from the dataChange listener also carries event data about the specific Gantt item that has been altered. We use the event data to identify the altered item and how it has been changed.

Next, we’ll create an updateMonday function that updates monday.com when the appropriate "add", "update", or "delete" event is fired.

Add the following code to the main.js file, below the createGantt function:

function updateMonday(event) {
  if(event.action == "add" && event.isChild == true){
    // if the task is a child task
    if(event.records[0].data.parentId != null && !event.records[0].data.parentId.startsWith("_generatedProjectModel")){
            const parent_id = event.parent.data.monday_id;
            const child_name = event.records[0].data.name;
            const child_start = event.records[0].data.startDate.toISOString().split('T')[0];
            const child_end = event.records[0].data.endDate.toISOString().split('T')[0];
            addTaskToMonday(event, parent_id, child_name, child_start, child_end);
        // if the task is a parent task
        } else {
            const parent_id = event.records[0].data.id;
            const parent_name = event.records[0].data.name;
            const parent_start = event.records[0].data.startDate.toISOString().split('T')[0];
            const parent_end = event.records[0].data.endDate.toISOString().split('T')[0];
            addParentTaskToMonday(event, parent_name, parent_start, parent_end);
        }
    } else if (event.action == "update"){
        if("startDate" in event.changes || "endDate" in event.changes || "name" in event.changes){
            if ("column_values" in event.records[0].data){
                if ("name" in event.changes){
                    var updateType = "name";
                } else if ("startDate" in event.changes || "endDate" in event.changes){
                    var updateType = "timeline";
                }
                const task_id = event.record.data.monday_id;
                const task_name = event.record.data.name;
                const task_start = event.record.data.startDate.toISOString().split('T')[0];
                const task_end = event.record.data.endDate.toISOString().split('T')[0];
                const board_id = event.record.data.board_id;
                const column_id = event.record.data.column_values[event.record.data.column_values.length - 1].id;
                updateTaskOnMonday(board_id, column_id, task_id, task_name, task_start, task_end, updateType);
            }
        }
    } else if (event.action == "remove"){
        if(event.isCollapse != true){
            const task_id = event.records[0].data.monday_id;
            deleteTask(task_id);
        }
    }
}

The above updateMonday function is called on all changes to the data store of Bryntum Gantt. We use it to call one of the monday.com API CRUD functions we defined, as follows:

  function updateMonday(event) {
    if(event.action == "add" && event.isChild == true){
      // if the task is a child task
      if(event.records[0].data.parentId != null && !event.records[0].data.parentId.startsWith("_generatedProjectModel")){
              const parent_id = event.parent.data.monday_id;
              const child_name = event.records[0].data.name;
              const child_start = event.records[0].data.startDate.toISOString().split('T')[0];
              const child_end = event.records[0].data.endDate.toISOString().split('T')[0];
              addTaskToMonday(event, parent_id, child_name, child_start, child_end);

If the item is a parent item, then addParentTaskToMonday is called with data from the event and the item is created on monday.com.

  } else {
      const parent_id = event.records[0].data.id;
      const parent_name = event.records[0].data.name;
      const parent_start = event.records[0].data.startDate.toISOString().split('T')[0];
      const parent_end = event.records[0].data.endDate.toISOString().split('T')[0];
      addParentTaskToMonday(event, parent_name, parent_start, parent_end);
  }
  } else if (event.action == "update"){
      if("startDate" in event.changes || "endDate" in event.changes || "name" in event.changes){
          if ("column_values" in event.records[0].data){
              if ("name" in event.changes){
                  var updateType = "name";
              } else if ("startDate" in event.changes || "endDate" in event.changes){
                  var updateType = "timeline";
              }
              const task_id = event.record.data.monday_id;
              const task_name = event.record.data.name;
              const task_start = event.record.data.startDate.toISOString().split('T')[0];
              const task_end = event.record.data.endDate.toISOString().split('T')[0];
              const board_id = event.record.data.board_id;
              const column_id = event.record.data.column_values[event.record.data.column_values.length - 1].id;
              updateTaskOnMonday(board_id, column_id, task_id, task_name, task_start, task_end, updateType);
          }
      }

If the dataChange event is a "remove" event, then we delete the matching monday.com item using the deleteTask function.

  } else if (event.action == "remove"){
      if (event.isCollapse != true) {
          const task_id = event.records[0].data.monday_id;
          deleteTask(task_id);
      }
  }

Finally, change the imports at the top of the main.js file to the following:

import { Gantt } from '@bryntum/gantt/gantt.module.js';
import { getTasksFromMonday, addTaskToMonday, updateTaskOnMonday, addParentTaskToMonday, deleteTask } from './graph.js';

Now try to create, update, delete, and edit an item in your Bryntum Gantt component. You’ll see the changes reflected in monday.com.

You can use the monday.com Gantt view layout to represent your data as a Gantt chart.

Next steps

This tutorial gives you a starting point for creating a Bryntum Gantt chart using vanilla JavaScript and syncing it with monday.com.

There are many ways you can improve your Bryntum Gantt chart. For example, you can add indenting and outdenting functionalities to the Bryntum Gantt, which change a task’s position in the task tree when changes are made to the task’s parent ID value.

You can also save changes to task dependencies, as monday.com supports task dependencies. However, you can’t save changes to the order of your tasks in the Bryntum Gantt, as the feature allowing you to use the monday.com API to change the order of board items is still being developed.

Take a look at our demos page to see the features available for you to add to your Bryntum Gantt.

Bryntum

Bryntum Gantt