Get Started
Introduction
Components
Accordion
Badge
Button
Card
Checkbox
Command
Dialog
Dropdown Menu
Input
Select
Switch
Table
Tabs
Toast
Tooltip
Forms
Introduction
Validation
Field Arrays

Async Infinite Scroll

IntersectionObserver-triggered pagination with <Async> streaming boundary, mapArray append, per-item like/save actions, and effect cleanup on unmount. Tests the IRAsync + mapArray compiler path, reactive list growth, and error/empty-state branches.

#Preview

8 articles
0 liked
0 saved
Page 1 / 5
MH

Type-Safe API Clients with Zod and TypeScript

TypeScript

Build end-to-end type safety from your OpenAPI spec to your React components using Zod schema inference.

Maya Hoffman
7 min read
2h ago
LP

React Server Components: A Practical Guide

React

Move data fetching to the server without sacrificing interactivity. How RSC changes the mental model for building apps.

Leon Park
12 min read
4h ago
SC

Core Web Vitals in 2025: What Changed

Performance

INP replaced FID. Interaction to Next Paint measures responsiveness more accurately. Here is how to optimize for it.

Sara Chen
9 min read
6h ago
JO

Testing the UI Layer Without a Browser

Testing

Snapshot testing is dead. Here is how to write IR-level component tests that run in milliseconds with zero flakiness.

James Okafor
6 min read
1d ago
PN

CSS Container Queries: Beyond Responsive Breakpoints

CSS

Container queries let components adapt to their own size, not the viewport. The end of media query hacks.

Priya Nair
8 min read
1d ago
TE

Zero-Downtime Deployments with Feature Flags

DevOps

Ship to production continuously without breaking users. A pragmatic guide to trunk-based development.

Tom Eriksen
11 min read
2d ago
AO

OWASP Top 10 for Frontend Developers

Security

XSS, CSRF, and clickjacking are still rampant. Concrete mitigations every frontend engineer should know.

Aisha Okonkwo
14 min read
2d ago
WZ

Micro-Frontends: Lessons from the Trenches

Architecture

Three years running a micro-frontend architecture at scale. What worked, what did not, and what we would do differently.

Wei Zhang
16 min read
3d ago
1"use client"23import { createSignal, createMemo, onMount, onCleanup } from '@barefootjs/client'45type Article = { id: number; title: string; liked: boolean; saved: boolean; /* ... */ }6type FetchStatus = 'idle' | 'loading' | 'error' | 'end'78export function InfiniteScrollDemo() {9  const [items, setItems] = createSignal<Article[]>(INITIAL_ITEMS)10  const [cursor, setCursor] = createSignal(1)11  const [status, setStatus] = createSignal<FetchStatus>('idle')1213  const totalCount = createMemo(() => items().length)14  const likedCount = createMemo(() => items().filter(a => a.liked).length)1516  const toggleLike = (id: number) => {17    setItems(prev => prev.map(a => a.id === id ? { ...a, liked: !a.liked } : a))18  }1920  const loadMore = async () => {21    if (status() === 'loading' || status() === 'end') return22    setStatus('loading')23    try {24      const newItems = await fetchPage(cursor())25      setItems(prev => [...prev, ...newItems]) // mapArray append26      setCursor(c => c + 1)27      setStatus('idle')28    } catch {29      setStatus('error')30    }31  }3233  onMount(() => {34    const sentinel = document.querySelector('.is-sentinel')35    const observer = new IntersectionObserver(36      entries => { if (entries[0].isIntersecting) loadMore() },37      { threshold: 0.1 }38    )39    observer.observe(sentinel!)40    onCleanup(() => observer.disconnect()) // effect cleanup on unmount41  })4243  return (44    <div>45      <div>{totalCount()} articles · {likedCount()} liked</div>4647      {/* <Async> boundary — IRAsync wrapping a signal-driven map */}48      <Async fallback={<ArticleSkeleton />}>49        <div data-slot="article-list">50          {items().map(article => (51            <article key={article.id}>52              <h3>{article.title}</h3>53              <button54                aria-pressed={article.liked ? 'true' : 'false'}55                onClick={() => toggleLike(article.id)}56              >57                {article.liked ? '♥' : '♡'}58              </button>59            </article>60          ))}61        </div>62      </Async>6364      {/* Sentinel + status branches */}65      <div className="is-sentinel">66        {status() === 'loading' ? <p>Loading…</p> : null}67        {status() === 'error'   ? <button onClick={loadMore}>Retry</button> : null}68        {status() === 'end'     ? <p>You have reached the end · {totalCount()} articles</p> : null}69      </div>70    </div>71  )72}

#Features

<Async> Boundary + mapArray

The initial article list is wrapped in an <Async fallback={skeleton}> boundary. In SSR the compiler emits a <Suspense> node (IRAsync → Hono adapter) containing the items().map() loop. This exercises the IRAsync + IRMap compiler path that was previously untested by any block demo.

IntersectionObserver + Effect Cleanup

onMount registers an IntersectionObserver on the sentinel div at the bottom of the list. onCleanup(() => observer.disconnect()) ensures the observer is torn down if the component unmounts mid-fetch, preventing stale callbacks and memory leaks.

mapArray Append

Each page load calls setItems(prev => [...prev, ...newItems]), appending to the existing signal array. BarefootJS's client-side mapArray reconciles the new items by keyed diffing — only the new DOM nodes are created; existing article cards are not re-rendered.

Error and Empty States

A createSignal<FetchStatus> drives three conditional branches: loading (spinner), error (retry button), and end (end-of-list message). The 12% simulated error rate makes the retry branch reachable during testing.

Per-Item Reactive Actions

Each article card has like and save toggles that call setItems(prev => prev.map(a => a.id === id ? ...)), an immutable update inside a reactive loop. createMemo chains derive aggregate counts (liked, saved) from the items signal, updating the stats bar reactively.