Skip to main content
@craft-ts
LearnLearn with EffectAI agentsGuideReference
@craft-ts/core@craft-ts/component@craft-ts/dev-tools@craft-ts/effect@craft-ts/style@craft-ts/style-testing@craft-ts/i18n@craft-ts/i18n-effect@craft-ts/mcp
ExamplesMigrationEffect compatibility & maturityAdopting CraftTS progressivelyPress kitRoadmapBacklog
Beta
  • Learn
  • Learn with Effect
  • AI agents
  • Guide
  • Reference
  • Packages
  • Resources
  • Guide overview
  • Create a project
i.Core concepts
  • The mental model
  • Which primitive should I use?
  • Anatomy of a primitive
  • Generators and yield*
  • Insertions
  • Typed insertion pipes
  • Exceptions as values
ii.Managing state
  • Local state
  • State machines
  • query
  • Mutations
  • queryParams
  • asyncProcess
  • Selecting
  • Reacting to mutations
  • Collections
  • Collection utilities
  • Persistence
  • GlobalPersisterHandler
  • Pagination placeholders
  • Schema validation
iii.Recommended approaches
  • Inject at the point of use
iv.Structuring the app
  • craftService
  • Service scopes
  • Shaping the public API
  • Abstract services
  • App start
  • Lazy services
  • Server functions
  • craftRegisterFor
  • Target wrapper
v.Routing & type-safe DI
  • Setup
  • CLI automation
  • ESLint rules
  • Route providers
  • Route guards
  • Route exception handling
  • Non-blocking navigation
  • Global error component
  • Route load errors
  • Scaling routes
vi.Components & templates
  • Components
  • Activating the style system
  • Typed styles
  • Defining a design system
  • Tokens and variables
  • Global foundation and fonts
  • Pseudo-elements and animations
  • Axes and the matrix
  • Context obligations
  • Testing visual states
  • Text contrast
  • Visual attestation
  • Template obligations
  • Typed CSS variables
  • Fine-grained reactivity
  • Progressive rendering
  • Directives and .pipe(...)
  • settledValue & pendingNode
  • Accessibility
  • Customization
  • Content projection
  • Styling a component
  • Template migrator
vii.Forms
  • Overview
  • Validators
  • Submitting
  • Nested forms
  • Exception handling
  • Complete examples
viii.Testing
  • Testing services
  • Testing components
  • Type-level tests
  • Browser boundaries
  • Page stability
  • Architecture rules
    • Overview
    • Declarative baseline
    • Unique identities
    • HTTP endpoint ownership
    • Computed purity
    • Dependency cycles
    • Mutation reactions
    • Route DI proofs
    • Route component files
    • Path boundaries
    • Exclusive links
    • Persisted identities
    • insertSelect keys
    • craftEffect and network
    • craftEffect and imperative sync
    • Interactive element names
    • Metric thresholds
    • Server-state loaders
    • Primitive loader requirements
  • Extensible architecture graph
  • Graph insights
  • Craft graph vs Nx
ix.Reactivity utilities
  • craftComputed
  • craftEffect
  • craftMethod
  • source$
  • on$
  • fromEventToSource$
  • sourceFromEvent
  • afterRecomputation
x.Internationalisation
  • Type-safe i18n
  • The catalogue
  • Tokens
  • The runtime
  • With Effect
xi.Deployment (experimental)
  • Overview
  • Manifest reference
  • Diagnostics
  • Providers
  • Alchemy provider
xii.Going further
  • SSR and hydration
  • Effect integration
  • Program operators
  • Pattern matching
  • Observability
  • AI agents
  • Temporal runtime
  1. Components & templates
  2. Progressive rendering

Progressive rendering with scheduleFor#

forNode renders synchronously by default. That is the right choice for short lists and keeps the initial behavior predictable. For a large collection, scheduleFor lets Craft spread fragment creation and updates over animation frames.

Basic usage#

ts
import { forNode, scheduleFor } from '@craft-ts/component';

forNode(cells, { track: (cell) => cell.id }, (cell) => renderCell(cell)).pipe(
  scheduleFor({
    enabled: true,
    strategy: 'frame',
    frameBudgetMs: 4,
  }),
);

The directive is attached to the forNode node. It does not add a DOM wrapper and does not change the item, index, dependency, exception, or pending-source contracts of the block.

When to use it#

Use strategy: 'frame' when the first visible items should appear quickly and the browser must keep handling input and painting while the rest of the list is created. A smaller frameBudgetMs yields more often; a larger budget completes the list sooner but can occupy the main thread for longer.

Disable it explicitly when a screen needs the synchronous behavior:

ts
forNode(items, { track: (item) => item.id }, renderItem).pipe(
  scheduleFor({ enabled: false, strategy: 'frame' }),
);

forNode without scheduleFor is already synchronous. The first delivery supports sync and frame; idle will be added with its fallback policy in a later delivery.

What scheduling does—and does not do#

Scheduling improves perceived responsiveness by yielding between batches. It does not reduce the total work required to create or update every fragment. Stable keys still control reconciliation, and existing keyed DOM fragments keep their identity when the collection is reordered.

For very large or continuously scrolling collections, prefer virtualisation: it reduces the number of DOM nodes and bindings that exist at the same time. Scheduling and virtualisation solve different problems and can eventually be combined.

Pixel Art Workshop#

The demo's Pixel Art Workshop uses frame scheduling for its 256-cell grid:

ts
forNode(INDEXES, { track: (index) => index }, renderCell).pipe(
  scheduleFor({ strategy: 'frame', frameBudgetMs: 4 }),
);

The production benchmark can compare the synchronous baseline and frame mode with 256, 1,000, and 10,000 cells. Its commands and metrics are documented in docs/benchmarks/schedule-for-pixel-art.md in the repository.

←Fine-grained reactivityDirectives and .pipe(...)→

On this page

  • Basic usage
  • When to use it
  • What scheduling does—and does not do
  • Pixel Art Workshop

@craft-ts · MIT licensed

GitHubnpm

Search