Blazorise Gantt: Editing

Enable built-in task editing or keep persistence and dialogs in your own page.

Set Editable to enable built-in create, add-child, edit, and delete flows. Use command switches and CommandAllowed for role-based or item-based command rules.

Set UseInternalEditing="false" when you want Gantt to keep its command affordances but let your page own the modal and persistence flow through NewItemClicked, AddChildItemClicked, EditItemClicked, and DeleteItemClicked.

Examples

Built-in Editing

Enable Editable to use built-in create, add-child, update, and delete workflows. This example also demonstrates command-level control through CommandAllowed.
Aug 31 - Sep 06, 2026
Task
Start
Marketing campaign
Aug 31, 2026
Research
Aug 31, 2026
Creative
Sep 04, 2026
Distribution
Sep 09, 2026
Mon 31
Tue 01
Wed 02
Thu 03
Fri 04
Sat 05
Sun 06
Marketing campaign45%
Research100%
Creative70%
<Gantt TItem="TaskItem"
       Data="@tasks"
       Date="@selectedDate"
       SelectedView="GanttView.Week"
       DurationField="Duration"
       Editable
       CommandAllowed="@AllowCommand">
    <GanttColumns>
        <GanttColumn Field="Title" Title="Task" Expandable Width="Width.Px(230)" />
        <GanttColumn Field="Start" Width="Width.Px(130)" />
        <GanttColumn Field="End" Width="Width.Px(130)" Visible="false" />
        <GanttColumn Field="Duration" Width="Width.Px(90)" TextAlignment="TextAlignment.Center" Visible="false" />
        <GanttColumn Field="Progress" Width="Width.Px(96)" TextAlignment="TextAlignment.Center" Visible="false" />
        <GanttCommandColumn Width="Width.Px(60)" />
    </GanttColumns>
    <GanttToolbar />
    <GanttViews>
        <GanttWeekView TimelineCellWidth="90" RowHeight="44" />
    </GanttViews>
</Gantt>
@code {
    private DateOnly selectedDate = DateOnly.FromDateTime( DateTime.Today );

    private List<TaskItem> tasks = CreateTasks();

    private static List<TaskItem> CreateTasks()
    {
        var baseDate = DateTime.Today.AddDays( -2 );

        var result = new List<TaskItem>
        {
            new() { Id = "1", Title = "Marketing campaign", Start = baseDate, End = baseDate.AddDays( 15 ), Progress = 45d },
            new() { Id = "2", ParentId = "1", Title = "Research", Start = baseDate, End = baseDate.AddDays( 4 ), Progress = 100d },
            new() { Id = "3", ParentId = "1", Title = "Creative", Start = baseDate.AddDays( 4 ), End = baseDate.AddDays( 9 ), Progress = 70d },
            new() { Id = "4", ParentId = "1", Title = "Distribution", Start = baseDate.AddDays( 9 ), End = baseDate.AddDays( 15 ), Progress = 20d },
        };

        foreach ( var item in result )
        {
            item.Duration = Math.Max( 1, (int)Math.Ceiling( ( item.End - item.Start ).TotalDays ) );
        }

        return result;
    }

    private bool AllowCommand( GanttCommandContext<TaskItem> context )
    {
        if ( context.CommandType == GanttCommandType.Delete && context.Item?.ParentId is null )
            return false;

        return true;
    }

    public class TaskItem
    {
        public string Id { get; set; }

        public string ParentId { get; set; }

        public string Title { get; set; }

        public DateTime Start { get; set; }

        public DateTime End { get; set; }

        public int Duration { get; set; }

        public double Progress { get; set; }
    }
}

External Editing

By setting the UseInternalEditing=false Gantt raises command callbacks with the relevant item and context, but leaves the editing UI and persistence flow to your implementation. This is ideal for keeping Gantt as a pure view component while owning the user experience and data flow of your app.
Task
Start
Website redesign
Aug 31, 2026
Discovery
Aug 31, 2026
Implementation
Sep 04, 2026
Launch
Sep 12, 2026
Mon 31
Tue 01
Wed 02
Thu 03
Fri 04
Sat 05
Sun 06
Website redesign
Discovery
Implementation

Last external action: None

<Gantt TItem="TaskItem"
       Data="@tasks"
       Date="@selectedDate"
       SelectedView="GanttView.Week"
       DurationField="Duration"
       Editable
       UseInternalEditing="false"
       NewItemClicked="@OnNewItemClicked"
       AddChildItemClicked="@OnAddChildItemClicked"
       EditItemClicked="@OnEditItemClicked"
       DeleteItemClicked="@OnDeleteItemClicked">
    <GanttColumns>
        <GanttColumn Field="Title" Title="Task" Expandable Width="Width.Px( 230 )" />
        <GanttColumn Field="Start" Width="Width.Px( 130 )" />
        <GanttColumn Field="End" Width="Width.Px( 130 )" Visible="false" />
        <GanttColumn Field="Duration" Width="Width.Px( 90 )" TextAlignment="TextAlignment.Center" Visible="false" />
        <GanttCommandColumn Width="Width.Px( 176 )">
            <HeaderTemplate>
                @if ( context.CanAddTask )
                {
                    <Button Size="Size.ExtraSmall" Color="Color.Success" Outline Clicked="@context.AddTask">New</Button>
                }
            </HeaderTemplate>
            <DisplayTemplate>
                @if ( context.CanAddChild )
                {
                    <Button Size="Size.ExtraSmall" Color="Color.Secondary" Outline Margin="Margin.Is1.FromEnd" Clicked="@context.AddChild">Child</Button>
                }
                @if ( context.CanEdit )
                {
                    <Button Size="Size.ExtraSmall" Color="Color.Primary" Outline Margin="Margin.Is1.FromEnd" Clicked="@context.Edit">Edit</Button>
                }
                @if ( context.CanDelete )
                {
                    <Button Size="Size.ExtraSmall" Color="Color.Danger" Outline Clicked="@context.Delete">Delete</Button>
                }
            </DisplayTemplate>
        </GanttCommandColumn>
    </GanttColumns>
    <GanttViews>
        <GanttWeekView TimelineCellWidth="90" RowHeight="44" />
    </GanttViews>
</Gantt>

<Paragraph TextColor="TextColor.Muted" Margin="Margin.Is3.FromTop">
    Last external action: @lastAction
</Paragraph>

<Modal @bind-Visible="@editorVisible" Centered>
    <ModalContent>
        <ModalHeader>
            <ModalTitle>@editorModeTitle</ModalTitle>
            <CloseButton Clicked="@CloseEditor" />
        </ModalHeader>
        <ModalBody>
            <Paragraph TextColor="TextColor.Muted">
                This modal is owned by the page. Gantt only raises the command callbacks.
            </Paragraph>
            <Field>
                <FieldLabel>Title</FieldLabel>
                <FieldBody>
                    <TextInput @bind-Value="@editorTitle" Immediate="false" Placeholder="Task title" />
                </FieldBody>
            </Field>
        </ModalBody>
        <ModalFooter>
            <Button Color="Color.Secondary" Clicked="@CloseEditor">Cancel</Button>
            <Button Color="Color.Primary" Clicked="@SaveEditor">Save</Button>
        </ModalFooter>
    </ModalContent>
</Modal>
@code {
    private DateOnly selectedDate = DateOnly.FromDateTime( DateTime.Today );
    private List<TaskItem> tasks = CreateTasks();
    private bool editorVisible;
    private string editorModeTitle = "Edit task";
    private string editorTitle;
    private string editorParentId;
    private string editorTargetId;
    private TaskItem editorDraft;
    private string lastAction = "None";
    private int nextId = 100;

    private Task OnNewItemClicked( GanttCommandContext<TaskItem> context )
        => OpenEditor( context.Item, null, null, "Create task" );

    private Task OnAddChildItemClicked( GanttCommandContext<TaskItem> context )
        => OpenEditor( context.Item, context.ParentItem, null, $"Create child for {context.ParentItem?.Title}" );

    private Task OnEditItemClicked( GanttItemClickedEventArgs<TaskItem> context )
        => OpenEditor( context.Item, null, context.Item?.Id, $"Edit {context.Item?.Title}" );

    private Task OnDeleteItemClicked( GanttItemClickedEventArgs<TaskItem> context )
    {
        if ( context?.Item?.Id is null )
            return Task.CompletedTask;

        RemoveTaskWithChildren( tasks, context.Item.Id );
        lastAction = $"Deleted {context.Item.Title}";

        return Task.CompletedTask;
    }

    private Task OpenEditor( TaskItem item, TaskItem parentItem, string targetId, string modeTitle )
    {
        editorDraft = item?.Clone() ?? new TaskItem();
        editorParentId = parentItem?.Id;
        editorTargetId = targetId;
        editorTitle = editorDraft.Title ?? string.Empty;
        editorModeTitle = modeTitle;
        editorVisible = true;

        return Task.CompletedTask;
    }

    private Task CloseEditor()
    {
        editorVisible = false;
        editorDraft = null;
        editorParentId = null;
        editorTargetId = null;
        editorTitle = string.Empty;

        return Task.CompletedTask;
    }

    private Task SaveEditor()
    {
        if ( editorDraft is null )
            return Task.CompletedTask;

        editorDraft.Title = string.IsNullOrWhiteSpace( editorTitle )
            ? "Untitled task"
            : editorTitle.Trim();

        if ( string.IsNullOrEmpty( editorTargetId ) )
        {
            editorDraft.Id ??= $"task-{nextId++}";
            editorDraft.ParentId = editorParentId;
            tasks.Add( editorDraft.Clone() );
            lastAction = editorParentId is null
                ? $"Created {editorDraft.Title}"
                : $"Created child {editorDraft.Title}";
        }
        else
        {
            var existingTask = tasks.FirstOrDefault( x => x.Id == editorTargetId );

            if ( existingTask is not null )
            {
                existingTask.Title = editorDraft.Title;
                lastAction = $"Updated {existingTask.Title}";
            }
        }

        return CloseEditor();
    }

    private static void RemoveTaskWithChildren( List<TaskItem> list, string itemId )
    {
        var idsToDelete = new HashSet<string>( StringComparer.Ordinal ) { itemId };
        var changed = true;

        while ( changed )
        {
            changed = false;

            foreach ( var task in list )
            {
                if ( task.ParentId is not null && idsToDelete.Contains( task.ParentId ) && idsToDelete.Add( task.Id ) )
                    changed = true;
            }
        }

        list.RemoveAll( x => idsToDelete.Contains( x.Id ) );
    }

    private static List<TaskItem> CreateTasks()
    {
        var baseDate = DateTime.Today.AddDays( -2 );

        return new List<TaskItem>
        {
            new() { Id = "1", Title = "Website redesign", Start = baseDate, End = baseDate.AddDays( 14 ), Duration = 14 },
            new() { Id = "2", ParentId = "1", Title = "Discovery", Start = baseDate, End = baseDate.AddDays( 4 ), Duration = 4 },
            new() { Id = "3", ParentId = "1", Title = "Implementation", Start = baseDate.AddDays( 4 ), End = baseDate.AddDays( 12 ), Duration = 8 },
            new() { Id = "4", ParentId = "1", Title = "Launch", Start = baseDate.AddDays( 12 ), End = baseDate.AddDays( 14 ), Duration = 2 },
        };
    }

    public class TaskItem
    {
        public string Id { get; set; }

        public string ParentId { get; set; }

        public string Title { get; set; }

        public DateTime Start { get; set; }

        public DateTime End { get; set; }

        public int Duration { get; set; }

        public TaskItem Clone()
        {
            return new TaskItem
            {
                Id = Id,
                ParentId = ParentId,
                Title = Title,
                Start = Start,
                End = End,
                Duration = Duration,
            };
        }
    }
}

API

See the API reference for the parameters, events, methods, and related types available to the components covered on this page.

On this page