mugen

Effects

Transform content into state — markdown, syntax highlighting, async loads.

Some content can't be rendered as primitives until it's transformed: markdown has to be parsed, code has to be highlighted, a remote value has to load. useMugenEffect does that transform and stores the result with useMugenState — and because a set re-measures the row, the height stays exact the moment the content resolves.

It's shaped like useEffect(effect, deps), return a cleanup — with one deliberate difference: it runs for every row, on- or off-screen, not just mounted ones. That's the point: row 9,000's markdown parses and its height becomes exact without ever scrolling to it.

Markdown in a row

import {
  , , ,
  , , ,
} from '@wingleeio/mugen';

interface Post {
  : string;
  : string;
}

declare function (: string): <string[]>;

function (: Post) {
  const [, ] = <string[]>([]);

  (() => {
    let  = false;
    (.).(() => {
      if (!) ();
    });
    return () => {
       = true;
    };
  }, [.]);

  return (
    < ={8} ={16}>
      {. === 0 ? (
        <>{'Loading…'}</>
      ) : (
        .((, ) => < ={}>{}</>)
      )}
    </>
  );
}

export function ({  }: { : Post[] }) {
  const  = ({ :  });
  return (
    < ={} ={() => .} ={}
      ="16px Inter" ={24} ="2xl" />
  );
}

The row first measures the Loading… placeholder. When parseMarkdown resolves, setBlocks replaces the blocks, mugen re-walks the row, and the scrollbar grows to the real, parsed height — no layout shift, no measure-on-mount.

notes.tsx
loading preview…

Highlighting code

The same shape — and pair it with useMugenMemo for the cheap synchronous split:

import { , , , ,  } from '@wingleeio/mugen';

interface Snippet {
  : string;
  : string;
  : string;
}

declare function (: string, : string): <string[]>;

export function (: Snippet) {
  // Cheap, synchronous fallback so the first measure is close.
  const  = (() => ..('\n'), [.]);
  const [, ] = <string[] | null>(null);

  (() => {
    let  = false;
    (., .).(() => {
      if (!) ();
    });
    return () => {
       = true;
    };
  }, [., .]);

  return (
    < ={2} ={12}>
      {( ?? ).((, ) => (
        < ={} ="13px 'JetBrains Mono'" ={20}>
          {}
        </>
      ))}
    </>
  );
}

Rules of thumb

  • Don't put the state you set into deps — that would loop. Watch the inputs (the source text), not the output.
  • Effects run off the measure pass on a microtask; a set re-measures the row.
  • Cancel async work in the returned cleanup (the cancelled flag above) — it runs before the next effect and when the row is removed.
  • Keep effect idempotent for a given deps — it may re-run when deps change.

On this page