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.
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
