Decorators
Ignite-Element provides powerful @Shared
and @Isolated
decorators for building modular, scalable web components. These decorators streamline integrating state management with rendering logic, allowing you to create maintainable and reusable components.
Enable Decorators in TypeScript
To use decorators in your project, make sure the following options are enabled in your tsconfig.json
:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Without these settings, TypeScript will not recognize decorators in your code.
Type Safety with RenderArgs
RenderArgs
Ignite-Element provides the RenderArgs
type helper to make your components type-safe when using decorators. You’ll need to pass typeof source
(e.g., your state machine or store) to RenderArgs
to ensure type inference.
Future Plans: We aim to infer types automatically for Ignite-Element decorators in the future, simplifying the setup further.
What Are Decorators
@Shared
: Creates components that share the same state across all instances. Ideal for global states like progress bars, dashboards, or game stats.@Isolated
: Creates components with independent states for each instance. Perfect for localized states like tab navigation, rating systems, or dynamic forms.
Shared Decorator
Overview
The @Shared
decorator connects a component to a global state, ensuring all component instances reflect and update the same state.
Example: Progress Bar Component
The following example demonstrates a progress bar that calculates the percentage of completed tasks based on the global state managed by a state machine.
import { Shared, RenderArgs } from "ignite-element";
import { html } from "lit-html";
import taskManagerMachine from "./taskManagerMachine";
@Shared("progress-bar")
export class ProgressBar {
render({ state }: RenderArgs<typeof taskManagerMachine>) {
const { tasks } = state.context;
const completed = tasks.filter((t) => t.completed).length;
const total = tasks.length;
const percentage = total > 0 ? (completed / total) * 100 : 0;
const backgroundStyle =
percentage === 100
? "background: #22c55e;"
: `background: linear-gradient(
90deg,
rgba(34, 197, 94, 1) 0%,
rgba(251, 191, 36, 1) 50%,
rgba(209, 213, 219, 0.1) 100%
);`;
return html`
<div class="p-4 bg-blue-100 border rounded-md mt-2 mb-2">
<h3 class="text-lg font-bold">Progress</h3>
<div class="w-full bg-gray-200 rounded-full h-4 overflow-hidden">
<div
class="h-4 rounded-full transition-all duration-700 ease-out"
style="width: ${percentage}%; ${backgroundStyle}"
></div>
</div>
<p class="mt-2">${completed}/${total} tasks completed</p>
</div>
`;
}
}
Isolated Decorator
Overview
The @Isolated
decorator connects a component to a state that is independent for each instance. This ensures that multiple component instances can coexist with unique behaviors and states.
Example 2: Rating Component
The RatingItem
component tracks its rating state independently for each instance.
import { Isolated } from "ignite-element";
import { RenderArgs } from "ignite-element";
import ratingMachine from "./ratingMachine";
@Isolated("rating-item")
export class RatingItem {
render({ state, send }: RenderArgs<typeof ratingMachine>) {
const { rating, maxRating } = state.context;
return html`
<div class="p-4 bg-white border rounded-md">
<h3 class="font-bold mb-2">Rate this item:</h3>
<div class="flex space-x-2">
${Array.from({ length: maxRating }, (_, i) =>
html`
<button
class="text-xl ${rating > i ? 'text-yellow-500' : 'text-gray-300'}"
@click=${() => send({ type: "RATE", value: i + 1 })}
>
★
</button>
`
)}
</div>
<p class="mt-2 text-sm text-gray-600">
You rated this ${rating}/${maxRating}.
</p>
</div>
`;
}
}
Best Practices
Centralize Logic:
Use state machines or stores to manage all data and interactions.
Choose the Right Decorator:
Use
@Shared
for global states (e.g., dashboards, progress bars).Use
@Isolated
for independent states (e.g., tabs, ratings).
Dynamic Contexts:
Configure state machines dynamically for greater flexibility.
Encapsulate Rendering:
Keep rendering logic focused and delegate state management to the source.
Last updated