npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-between

v0.1.1

Published

Insert dividers between React children that handles conditionally rendered components

Downloads

20

Readme

react-between

CI npm version unpacked size license

Insert dividers between React children that actually handles conditionally rendered components. Zero extra DOM wrappers.

The Problem

Every existing separator solution works at the React element level — they intersperse separators using React.Children.toArray(). But React can't look ahead to see what a component renders. When a child returns null, you get consecutive separators:

// Any existing library (react-with-separator, Chakra Stack, MUI Stack, etc.)
<Stack divider={<hr />}>
    <Header /> {/* renders content */}
    <PromoBar /> {/* returns null — user dismissed it */}
    <Content /> {/* renders content */}
</Stack>

// Result in the DOM:
// Header
// ──────── ← divider
// ──────── ← divider (unwanted — PromoBar is gone)
// Content

This is a known React limitation. The React team confirmed it's by design — parents can't inspect a child's render output before rendering it.

The Solution

react-between checks the actual DOM after render. Using useLayoutEffect and MutationObserver, it detects which children produced visible output and hides dividers that have nothing between them — synchronously, before the browser paints.

import { Interpose } from 'react-between';

<Interpose divider={<hr />}>
    <Header />
    <PromoBar /> {/* returns null — divider is automatically hidden */}
    <Content />
</Interpose>;

// Result in the DOM:
// Header
// ──────── ← single divider, as expected
// Content

No consecutive separators, ever. Children toggle between content and null at any time — dividers adjust automatically.

This is a hack, and that's okay

Let's be honest: walking the DOM after render to fix what React can't express declaratively is a hack. It works, it's reliable, but it's working around a fundamental gap in React's component model. The alternative is pre-filtering children or lifting conditional logic to the parent — which is simpler and has zero runtime cost.

Use react-between when the simple approach doesn't work. Don't use it as a default.

When you don't need it

Just filter children at the call site:

<Stack divider={<hr />}>
    {showHeader && <Header />}
    {showPromo && <PromoBar />}
    {showContent && <Content />}
</Stack>

This handles the majority of cases with zero overhead. react-between exists for the remaining cases where:

  • Children are components that conditionally return null internally — the parent can't know at render time
  • Children toggle between content and null dynamically (timers, API responses, user interactions)
  • You're building a design system and can't control what consumers pass as children

If you control the render logic and can filter before passing children — do that instead.

Install

npm install react-between
pnpm add react-between
yarn add react-between

Requires react >= 16.8.0 and react-dom >= 16.8.0 as peer dependencies. No other runtime dependencies.

Quick Start

import { Interpose } from 'react-between';

function App() {
    return (
        <div className="stack">
            <Interpose divider={<hr />}>
                <Header />
                <Sidebar />
                <Content />
                <Footer />
            </Interpose>
        </div>
    );
}

Children that return null are skipped — no extra dividers appear.

Divider Types

Intrinsic element

The simplest form. The element is cloned with an internal ref — no wrappers added to the DOM.

<Interpose divider={<hr className="separator" />}>

Function with ref

For full control over the divider element. The function receives a ref (must be attached to the root DOM element) and the divider index:

<Interpose divider={(ref, index) => (
    <hr ref={ref} className={index === 0 ? 'first' : 'separator'} />
)}>

Primitive (string)

Works for simple text separators. Wrapped in a minimal <span style="display:contents"> since primitives can't hold refs.

<nav className="breadcrumb">
    <Interpose divider=" / ">
        <a href="/">Home</a>
        <a href="/products">Products</a>
        <span>Current Page</span>
    </Interpose>
</nav>

// Home / Products / Current Page

Component dividers

If you pass a component as a divider, it must support ref forwarding — either via forwardRef (React < 19) or the ref prop (React 19+). The ref must be attached to the component's root DOM element. Without it, the reconciler can't track the divider in the DOM.

// React 19+ — ref is a regular prop
function FancyDivider({ ref }: { ref?: React.Ref<HTMLHRElement> }) {
    return <hr ref={ref} className="fancy" />;
}

// React < 19 — use forwardRef
const FancyDivider = React.forwardRef<HTMLHRElement>((props, ref) => (
    <hr ref={ref} className="fancy" />
));

// Usage — pass as intrinsic element, ref is cloned automatically
<Interpose divider={<FancyDivider />}>

Leading & Trailing Dividers

Add dividers before the first and/or after the last visible child:

<Interpose divider={<hr />} leading trailing>
    <Section />
    <Section />
</Interpose>

// ────────
// Section
// ────────
// Section
// ────────

Leading and trailing dividers are also hidden when their adjacent child renders null.

API

| Prop | Type | Default | Description | | ---------- | ---------------------------------------- | ---------- | ----------------------------------------- | | divider | ReactNode \| (ref, index) => ReactNode | required | Element inserted between visible children | | leading | boolean | false | Divider before the first visible child | | trailing | boolean | false | Divider after the last visible child | | children | ReactNode | — | Children to interpose dividers between |

Interpose renders a Fragment — no wrapper element. Compose it inside your own container.

| Export | Description | | ---------------- | --------------------- | | Interpose | The component | | InterposeProps | TypeScript props type |

How It Works

  1. Interpose renders children and dividers in order. Dividers are tracked via refs — no data attributes or wrapper elements are added to your markup.
  2. After render, useLayoutEffect runs a reconciler that walks the DOM, splits it into "slots" between dividers, and checks each slot for visible content (element or text nodes).
  3. Dividers adjacent to empty slots are hidden with style.display = 'none'. Visible dividers retain their original display value.
  4. A MutationObserver watches for subtree changes — if a child toggles between content and null at any point, dividers re-reconcile automatically.

All of this happens synchronously before the browser paints, so there is no flash of consecutive dividers.

Comparison with Alternatives

| Library | Handles null children | Wrapper elements | Approach | | -------------------- | --------------------------------------------------------------------------- | ---------------- | ------------------------ | | react-between | Yes | None | DOM-level detection | | react-with-separator | No | None | React.Children.toArray | | react-group | No | None | React.Children.toArray | | react-extras (Join) | No | None | React.Children.toArray | | Chakra UI Stack | No (known issue) | Container | React.Children.toArray | | MUI Stack | No (known issue) | Container | React.Children.toArray |

Every existing solution operates at the React element level. react-between is the only one that checks the actual DOM output.

Trade-offs

This library trades runtime work for declarative convenience. Here's what you're paying:

| Concern | Impact | Details | | ----------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | MutationObserver | Minimal | Batched callbacks, ~0% memory overhead. Watches the parent element for subtree changes. | | useLayoutEffect | Minimal | Blocks paint for DOM reconciliation (~1ms typical). This is what prevents the flash of consecutive dividers. | | Sentinel spans | Two hidden spans | <span style="display:none"> at start/end of children. Invisible to layout and screen readers. | | SSR | Dividers flash on hydration | Server renders all dividers visible. After hydration, useLayoutEffect hides the extras. Brief flash possible. | | RSC | Requires 'use client' | DOM detection is inherently client-side. Wrap in a client component. | | No MutationObserver | Graceful fallback | Without MutationObserver (e.g. older environments), reconciliation runs only on React re-renders. Subtree changes from outside React won't be caught. | | Component dividers | Must forward ref | Component dividers need forwardRef (React < 19) or ref prop (React 19+). Without it, the reconciler can't track the divider. |

License

MIT