Build a Blazor todo list app

By Daniel Roth and Luke Latham

This tutorial shows you how to build and modify a Blazor app. You learn how to:

  • Create a todo list Blazor app project
  • Modify Razor components
  • Use event handling and data binding in components
  • Use routing in a Blazor app

At the end of this tutorial, you'll have a working todo list app.

Prerequisites

.NET Core 3.1 SDK or later

Create a todo list Blazor app

  1. Create a new Blazor app named TodoList in a command shell:

    dotnet new blazorserver -o TodoList
    

    The preceding command creates a folder named TodoList to hold the app. Change directories to the TodoList folder with the following command:

    cd TodoList
    
  2. Add a new Todo Razor component to the app in the Pages folder using the following command:

    dotnet new razorcomponent -n Todo -o Pages
    

    Important

    Razor component file names require a capitalized first letter, so confirm that the Todo component file name starts with a capital letter T.

  3. In Pages/Todo.razor provide the initial markup for the component:

    @page "/todo"
    
    <h3>Todo</h3>
    
  4. Add the Todo component to the navigation bar.

    The NavMenu component (Shared/NavMenu.razor) is used in the app's layout. Layouts are components that allow you to avoid duplication of content in the app.

    Add a <NavLink> element for the Todo component by adding the following list item markup below the existing list items in the Shared/NavMenu.razor file:

    <li class="nav-item px-3">
        <NavLink class="nav-link" href="todo">
            <span class="oi oi-list-rich" aria-hidden="true"></span> Todo
        </NavLink>
    </li>
    
  5. Rebuild and run the app. Visit the new Todo page to confirm that the link to the Todo component works.

  6. Add a TodoItem.cs file to the root of the project to hold a class that represents a todo item. Use the following C# code for the TodoItem class:

    public class TodoItem
    {
        public string Title { get; set; }
        public bool IsDone { get; set; }
    }
    
  7. Return to the Todo component (Pages/Todo.razor):

    • Add a field for the todo items in an @code block. The Todo component uses this field to maintain the state of the todo list.
    • Add unordered list markup and a foreach loop to render each todo item as a list item (<li>).
    @page "/todo"
    
    <h3>Todo</h3>
    
    <ul>
        @foreach (var todo in todos)
        {
            <li>@todo.Title</li>
        }
    </ul>
    
    @code {
        private IList<TodoItem> todos = new List<TodoItem>();
    }
    
  8. The app requires UI elements for adding todo items to the list. Add a text input (<input>) and a button (<button>) below the unordered list (<ul>...</ul>):

    @page "/todo"
    
    <h3>Todo</h3>
    
    <ul>
        @foreach (var todo in todos)
        {
            <li>@todo.Title</li>
        }
    </ul>
    
    <input placeholder="Something todo" />
    <button>Add todo</button>
    
    @code {
        private IList<TodoItem> todos = new List<TodoItem>();
    }
    
  9. Rebuild and run the app. When the Add todo button is selected, nothing happens because an event handler isn't wired up to the button.

  10. Add an AddTodo method to the Todo component and register it for button selections using the @onclick attribute. The AddTodo C# method is called when the button is selected:

    <input placeholder="Something todo" />
    <button @onclick="AddTodo">Add todo</button>
    
    @code {
        private IList<TodoItem> todos = new List<TodoItem>();
    
        private void AddTodo()
        {
            // Todo: Add the todo
        }
    }
    
  11. To get the title of the new todo item, add a newTodo string field at the top of the @code block and bind it to the value of the text input using the bind attribute in the <input> element:

    private IList<TodoItem> todos = new List<TodoItem>();
    private string newTodo;
    
    <input placeholder="Something todo" @bind="newTodo" />
    
  12. Update the AddTodo method to add the TodoItem with the specified title to the list. Clear the value of the text input by setting newTodo to an empty string:

    @page "/todo"
    
    <h3>Todo</h3>
    
    <ul>
        @foreach (var todo in todos)
        {
            <li>@todo.Title</li>
        }
    </ul>
    
    <input placeholder="Something todo" @bind="newTodo" />
    <button @onclick="AddTodo">Add todo</button>
    
    @code {
        private IList<TodoItem> todos = new List<TodoItem>();
        private string newTodo;
    
        private void AddTodo()
        {
            if (!string.IsNullOrWhiteSpace(newTodo))
            {
                todos.Add(new TodoItem { Title = newTodo });
                newTodo = string.Empty;
            }
        }
    }
    
  13. Rebuild and run the app. Add some todo items to the todo list to test the new code.

  14. The title text for each todo item can be made editable, and a check box can help the user keep track of completed items. Add a check box input for each todo item and bind its value to the IsDone property. Change @todo.Title to an <input> element bound to @todo.Title:

    <ul>
        @foreach (var todo in todos)
        {
            <li>
                <input type="checkbox" @bind="todo.IsDone" />
                <input @bind="todo.Title" />
            </li>
        }
    </ul>
    
  15. To verify that these values are bound, update the <h3> header to show a count of the number of todo items that aren't complete (IsDone is false).

    <h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>
    
  16. The completed Todo component (Pages/Todo.razor):

    @page "/todo"
    
    <h3>Todo (@todos.Count(todo => !todo.IsDone))</h3>
    
    <ul>
        @foreach (var todo in todos)
        {
            <li>
                <input type="checkbox" @bind="todo.IsDone" />
                <input @bind="todo.Title" />
            </li>
        }
    </ul>
    
    <input placeholder="Something todo" @bind="newTodo" />
    <button @onclick="AddTodo">Add todo</button>
    
    @code {
        private IList<TodoItem> todos = new List<TodoItem>();
        private string newTodo;
    
        private void AddTodo()
        {
            if (!string.IsNullOrWhiteSpace(newTodo))
            {
                todos.Add(new TodoItem { Title = newTodo });
                newTodo = string.Empty;
            }
        }
    }
    
  17. Rebuild and run the app. Add todo items to test the new code.

Next steps

In this tutorial, you learned how to:

  • Create a todo list Blazor app project
  • Modify Razor components
  • Use event handling and data binding in components
  • Use routing in a Blazor app

Learn about tooling for ASP.NET Core Blazor: