Group tasks
The functionality is available in PRO Edition only
You can group tasks by any field to let end users view them by responsible person, status, priority, or any other property. Only one grouping field is active at a time.
Group by resource
Call the group-tasks action with field: "resource" to place tasks under their assigned resources:
api.exec("group-tasks", { field: "resource" });
Tasks appear under their assigned leaf resources only. Parent group resources act as containers. You can't assign tasks to them, and Gantt doesn't render empty groups.
To apply grouping at startup, set the groupBy property:
<Gantt
{tasks}
{resources}
{assignments}
groupBy={{ field: "resource" }}
/>
Group by task field
Pass any task field name to group by that property. Each group takes its label from the corresponding field values in the data:
api.exec("group-tasks", { field: "priority" });
api.exec("group-tasks", { field: "status" });
Clear grouping
To remove the active grouping and restore the default task tree, call group-tasks with field: null:
api.exec("group-tasks", { field: null });
Grouping options
You can control how groups are built and where tasks appear with these options. All options can be combined in a single call. Pass any combination to the group-tasks action:
api.exec("group-tasks", {
field: "resource",
resourceHierarchy: false, // flat list (default: true = hierarchy preserved)
multipleResources: true, // task appears once under combined group
taskHierarchy: false, // flat list of tasks within groups
ungrouped: "top", // unassigned tasks at the top
});
Tasks assigned to multiple resources
By default, a task assigned to multiple resources appears under each resource separately. Set multipleResources to true to show it once under a combined group label (e.g., "Alice, Bob") instead:
// Default — task appears under each assigned resource
api.exec("group-tasks", { field: "resource" });
// Combined — task appears once under "Alice, Bob"
api.exec("group-tasks", {
field: "resource",
multipleResources: true,
});
Gantt places combined groups after all individual resource groups.
Ungrouped tasks
Tasks with no value for the grouping field appear in an "Ungrouped" section at the bottom by default. Use ungrouped to move them to the top or hide them:
api.exec("group-tasks", {
field: "priority",
ungrouped: "top", // "top" | "bottom" (default) | false
});
Resource hierarchy
By default, the resource tree structure (teams and members) is preserved when grouping by resource. Set resourceHierarchy to false to display resources as a flat list instead:
api.exec("group-tasks", {
field: "resource",
resourceHierarchy: false,
});
This option applies only when field is "resource".
Task hierarchy
By default, tasks appear as a flat list within each group. Set taskHierarchy to true to preserve the parent/child task structure:
api.exec("group-tasks", {
field: "resource",
taskHierarchy: true,
});
Custom value resolver
To extract a group value with custom logic instead of reading task[field] directly, pass a resolver function:
api.exec("group-tasks", {
field: "owner",
resolver: task => task.meta?.owner ?? "Unassigned",
});
This option doesn't apply when field is "resource".
Customizing grouped cell content
Default behavior
When grouping is active, group header rows are rendered by the built-in cell component. Its output depends on the grouping field:
- Resource grouping — shows the resource avatar and name; tasks assigned to multiple resources show stacked avatars
- Other fields — shows the raw field value as a string (e.g.,
"1"forpriority: 1) - Tasks with no value — shows "Ungrouped"
For resource grouping the default is usually sufficient. For other fields the raw value is displayed. If you want to change it, add a custom cell component.
Custom cell component
To override the default cell rendering, pass a custom Svelte component to the cell property of a column. Inside the component, use row.$group to detect group header rows and row.$groupValue to read the group value. The active grouping config is available through the "gantt-store" context:
<script>
import { getContext } from "svelte";
const api = getContext("gantt-store");
const { groupBy } = api.getReactiveState();
let { row } = $props();
const isGroup = $derived($groupBy?.field && row.$group);
</script>
{#if isGroup}
{#if row.$groupValue === "$ungrouped"}
Ungrouped
{:else}
{$groupBy.field}: {row.$groupValue}
{/if}
{:else}
{row.text}
{/if}
Apply the component to a column via the cell property:
<script>
import { Gantt } from "@svar-ui/svelte-gantt";
import GroupCell from "./GroupCell.svelte";
const columns = [
{ id: "text", header: "Task name", flexgrow: 1, cell: GroupCell },
];
</script>
<Gantt {tasks} {links} {scales} {columns} groupBy={{ field: "priority" }} />
Mapping raw values to labels
When grouping by a numeric or coded field, the raw value is rarely the right display text. Define a map of raw values to readable labels and use a $derived value to resolve the label before rendering:
<script>
import { getContext } from "svelte";
const api = getContext("gantt-store");
const { groupBy } = api.getReactiveState();
let { row } = $props();
const priorityMap = { 1: "Low", 2: "Medium", 3: "High" };
const isGroup = $derived($groupBy?.field && row.$group);
const label = $derived.by(() => {
const value = row.$groupValue;
if ($groupBy?.field === "priority") return priorityMap[value] ?? value;
return value;
});
</script>
{#if isGroup}
{#if row.$groupValue === "$ungrouped"}
Ungrouped
{:else}
{label}
{/if}
{:else}
{row.text}
{/if}
Limitations in grouped view
When grouping is active, task order differs from the actual tree and many editing actions are unavailable (they are disabled in the built-in menu and toolbar). If you customize the menu or toolbar using getMenuOptions or getToolbarButtons, pass { group: true } to get only the options that are valid in a grouped view:
const menu = getMenuOptions({ group: true });
const toolbar = getToolbarButtons({ group: true });
Related articles: