or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

built-in-components.mdcaching.mdchildren.mdcomponents.mdcomposition.mdcontext.mddevelopment.mdelements.mdexperimental.mdhooks-context.mdhooks-effects.mdhooks-imperative.mdhooks-performance.mdhooks-state.mdhooks-transitions.mdindex.mdrefs.mdtransitions.md
tile.json

composition.mddocs/

Component Composition

Optimize components with memo (skip re-renders) and lazy (code splitting). Use memo only when profiling shows performance issues.

memo

Prevent re-renders with shallow prop comparison. Only use when component is expensive to render.

function memo<P>(
  Component: ComponentType<P>,
  propsAreEqual?: (prevProps: P, nextProps: P) => boolean
): ComponentType<P>;
const ExpensiveComponent = memo(({ data }) => {
  return <div>{data.map(item => <Item key={item.id} {...item} />)}</div>;
});

// Custom comparison (advanced)
const User = memo(({ user, theme }) => <div style={{color: theme}}>{user.name}</div>,
  (prev, next) => prev.user.id === next.user.id  // Only re-render if user.id changes
);

// ⚠️ Common mistakes
// - Passing inline objects/arrays as props defeats memo (use useMemo)
// - Don't wrap every component - adds overhead without benefit
// - Profile first, then optimize

lazy

Code splitting.

function lazy<P>(factory: () => Promise<{ default: ComponentType<P> }>): ComponentType<P>;
const LazyComponent = lazy(() => import('./Component'));

function App() {
  return (
    <Suspense fallback={<Loader />}>
      <LazyComponent />
    </Suspense>
  );
}

// Named exports
const MyComponent = lazy(() =>
  import('./Components').then(m => ({ default: m.MyComponent }))
);

// Error handling: wrap in ErrorBoundary
<ErrorBoundary fallback={<ErrorUI />}>
  <Suspense fallback={<Loader />}>
    <LazyComponent />
  </Suspense>
</ErrorBoundary>