Lit Element Task Controller

The Task Controller in Lit is a helper for managing async operations (like API calls) directly inside your components, with built-in support for loading, success, and error states. Instead of manually tracking whether your data is loading, has failed, or succeeded, the Task Controller wraps all that logic in a neat package.

Why use it?

Let’s say you're building a component that fetches some data from an API. You normally would:

  • Store the data in a property.

  • Have flags like isLoading, hasError, etc.

  • Manually trigger updates and handle states.

With Task, all of that gets centralized and declarative.

Example without Task Controller

In the example you can see that to consume a REST API you need reactive states to handle if it is loading, the request data and also the error handling within the firstUpdated method that is executed only once when the component is already created.

import {html, css, LitElement} from 'lit';
import { map } from 'lit/directives/map.js'
import { when } from 'lit/directives/when.js'
import {customElement, state} from 'lit/decorators.js';

@customElement('users-list')
export class UserList extends LitElement {

  @state()
  loading = false;

  @state()
  error = null;

  @state()
  users = []

  async firstUpdated() {
    try {
      this.loading = true
      const response = await fetch('https://jsonplaceholder.typicode.com/users')
      this.users = await response.json()
    } catch {
      this.error = 'Ocurred a error'
    } finally {
      this.loading = false
    }
  }


  render() {
    return html`
        ${when(this.loading, () => html`Loading....`)}
        ${when(!this.loading && !this.error, () => html`
            <ul>
              ${map(this.users, ({name}) => html`<li>${name}</li>`)}
            </ul>
          `)}
        ${when(this.error, () => html`<p>${this.error}</p>`)}
    `;
  }
}

Example wit Task Controller

Task Controller greatly simplifies calls to REST APIs, all within the same class, managing the component lifecycle allowing it to be executed when the component is loaded or deferred, and also reacts to property changes.

import {html, css, LitElement} from 'lit';
import {customElement} from 'lit/decorators.js';
import { map } from 'lit/directives/map.js'
import {Task} from '@lit/task';


@customElement('user-list-task')
export class UserListTask extends LitElement {

  #task = new Task(this, {
    task: async () => {
      const response = await fetch('https://jsonplaceholder.typicode.com/users')
      return response.json()
    },
    args: () => []
  });

  render() {
    return this.#task.render({
      pending: () => html`Loading...`,
      complete: (users) => html`
        <ul>
         ${map(users, ({name}) => html`<li>${name}</li>`)}
        </ul>
      `,
      error: (e) => html`<p>Error: ${e}</p>`
    });
  }
}

To learn more about the world of Web Components and Lit, I leave the following link:

https://lit.dev/docs/data/task/

I recently created a fundraiser if you would like to help me.

https://linktr.ee/jorelstudioscode

0
Subscribe to my newsletter

Read articles from Jorel Studios Code directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jorel Studios Code
Jorel Studios Code