> ## Documentation Index
> Fetch the complete documentation index at: https://prefab.prefect.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Histogram

> Visualize the distribution of numeric data with automatic binning.

export const ComponentPreview = ({json, height, resizable, bare, hideJson, playground, children}) => {
  const hostRef = React.useRef(null);
  const handleRef = React.useRef(null);
  const cardRef = React.useRef(null);
  const containerRef = React.useRef(null);
  const [previewWidth, setPreviewWidth] = React.useState(null);
  const [isDragging, setIsDragging] = React.useState(false);
  const [playgroundHref, setPlaygroundHref] = React.useState(null);
  React.useEffect(function () {
    if (!playground) return;
    var isLocal = location.hostname === "localhost" || location.hostname === "127.0.0.1";
    var path = isLocal ? "/playground" : "/docs/playground";
    var url = new URL(path, window.location.href);
    url.hash = "code=" + playground;
    setPlaygroundHref(url.href);
  }, [playground]);
  var jsonStr = typeof json === "string" ? json : JSON.stringify(json, null, 2);
  React.useEffect(function () {
    if (cardRef.current) {
      var card = cardRef.current.closest(".group");
      if (card) card.classList.remove("group");
    }
    var host = hostRef.current;
    if (!host || !json) return;
    function mount() {
      if (!window.__prefab || !host) return;
      var dark = document.documentElement.classList.contains("dark");
      handleRef.current = window.__prefab.mountPreview(host, jsonStr, {
        dark: dark
      });
      if (bare && host.shadowRoot) {
        var m = host.shadowRoot.querySelector("[data-prefab-mount]");
        if (m) m.style.background = "transparent";
      }
    }
    if (window.__prefab) {
      mount();
    } else {
      if (!window.__prefabLoading) {
        if (!window.__prefabReady) {
          var s = document.createElement("script");
          s.src = "/renderer.js";
          document.head.appendChild(s);
        }
        window.__prefabLoading = new Promise(function (resolve) {
          function check() {
            if (window.__prefabReady) {
              window.__prefabReady.then(resolve);
            } else {
              setTimeout(check, 10);
            }
          }
          check();
        });
      }
      window.__prefabLoading.then(mount);
    }
    var observer = new MutationObserver(function () {
      var dark = document.documentElement.classList.contains("dark");
      if (handleRef.current) handleRef.current.setDark(dark);
    });
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return function () {
      observer.disconnect();
      if (handleRef.current) {
        handleRef.current.unmount();
        handleRef.current = null;
      }
    };
  }, [json]);
  function startResize(e) {
    e.preventDefault();
    var startX = e.clientX;
    var startW = hostRef.current ? hostRef.current.offsetWidth : 0;
    var maxW = containerRef.current ? containerRef.current.offsetWidth - 10 : startW;
    setIsDragging(true);
    function onMove(ev) {
      var w = Math.max(260, Math.min(startW + (ev.clientX - startX), maxW));
      setPreviewWidth(w);
    }
    function onUp() {
      document.removeEventListener("mousemove", onMove);
      document.removeEventListener("mouseup", onUp);
      setIsDragging(false);
    }
    document.addEventListener("mousemove", onMove);
    document.addEventListener("mouseup", onUp);
  }
  if (bare) {
    return <div ref={hostRef} />;
  }
  if (resizable) {
    return <Card>
        <div ref={cardRef} />
        <div ref={containerRef} style={{
      display: "flex",
      position: "relative",
      overflow: "hidden"
    }}>
          <div ref={hostRef} style={{
      flex: previewWidth ? "none" : 1,
      width: previewWidth ? previewWidth + "px" : undefined,
      minWidth: 0
    }} />
          <div onMouseDown={startResize} style={{
      width: "14px",
      flexShrink: 0,
      cursor: "col-resize",
      background: isDragging ? "var(--border, #e5e7eb)" : "color-mix(in srgb, var(--border, #e5e7eb) 40%, transparent)",
      display: "flex",
      alignItems: "center",
      justifyContent: "center",
      transition: "background 0.15s",
      userSelect: "none"
    }}>
            <svg width="6" height="24" style={{
      opacity: 0.5
    }}>
              <circle cx="2" cy="4" r="1.2" fill="currentColor" />
              <circle cx="2" cy="9" r="1.2" fill="currentColor" />
              <circle cx="2" cy="14" r="1.2" fill="currentColor" />
              <circle cx="2" cy="19" r="1.2" fill="currentColor" />
              <circle cx="5" cy="4" r="1.2" fill="currentColor" />
              <circle cx="5" cy="9" r="1.2" fill="currentColor" />
              <circle cx="5" cy="14" r="1.2" fill="currentColor" />
              <circle cx="5" cy="19" r="1.2" fill="currentColor" />
            </svg>
          </div>
          <div style={{
      flex: previewWidth ? 1 : "0 0 24px",
      minWidth: "24px",
      background: "repeating-linear-gradient(-45deg, transparent, transparent 3px, var(--border, #e5e7eb) 3px, var(--border, #e5e7eb) 4px)",
      opacity: 0.4
    }} />
          {previewWidth && <div style={{
      position: "absolute",
      bottom: "8px",
      right: "8px",
      background: "var(--background, white)",
      border: "1px solid var(--border, #e5e7eb)",
      borderRadius: "4px",
      padding: "1px 6px",
      fontSize: "11px",
      fontFamily: "monospace",
      color: "var(--muted-foreground, #6b7280)",
      pointerEvents: "none"
    }}>
              {previewWidth}px
            </div>}
        </div>
        {playgroundHref && <div style={{
      display: "flex",
      justifyContent: "flex-end",
      padding: "4px 0 8px"
    }}>
            <a href={playgroundHref} target="_blank" rel="noopener noreferrer" style={{
      fontSize: "11px",
      opacity: 0.4,
      textDecoration: "none",
      textDecorationLine: "none",
      borderBottom: "none",
      boxShadow: "none",
      display: "inline-flex",
      alignItems: "center",
      gap: "3px",
      transition: "opacity 0.15s",
      fontWeight: 400
    }} onMouseOver={function (e) {
      e.currentTarget.style.opacity = "0.6";
    }} onMouseOut={function (e) {
      e.currentTarget.style.opacity = "0.4";
    }}>
              Edit in Playground
              <svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
            </a>
          </div>}
        <div style={{
      marginBottom: "-2rem"
    }}>{children}</div>
      </Card>;
  }
  return <Card>
      <div ref={cardRef} />
      <div ref={hostRef} />
      {playgroundHref && <div style={{
    display: "flex",
    justifyContent: "flex-end",
    padding: "4px 0 8px"
  }}>
          <a href={playgroundHref} target="_blank" rel="noopener noreferrer" style={{
    fontSize: "11px",
    opacity: 0.4,
    textDecoration: "none",
    textDecorationLine: "none",
    borderBottom: "none",
    boxShadow: "none",
    display: "inline-flex",
    alignItems: "center",
    gap: "3px",
    transition: "opacity 0.15s",
    fontWeight: 400
  }} onMouseOver={function (e) {
    e.currentTarget.style.opacity = "0.6";
  }} onMouseOut={function (e) {
    e.currentTarget.style.opacity = "0.4";
  }}>
            Edit in Playground
            <svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M15 3h6v6" /><path d="M10 14 21 3" /><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></svg>
          </a>
        </div>}
      <div style={{
    marginBottom: "-2rem"
  }}>{children}</div>
    </Card>;
};

Histogram takes a list of raw numeric values, groups them into bins, and renders the result as a bar chart. Where a bar chart compares named categories, a histogram answers a different question: "how is this data distributed?" You give it numbers, it figures out the ranges and counts. All the binning math happens in Python at construction time -- the renderer receives a standard BarChart payload, so there's no new component to learn on the wire.

## Basic Usage

Pass a list of numbers and Histogram divides them into 10 equal-width bins by default. Each bar represents a range, and its height shows how many values fall within that range. The shape of the resulting chart immediately reveals the distribution: a tall cluster on the left with a long tail to the right suggests most values are fast, but some outliers are significantly slower.

<ComponentPreview json={{"view":{"type":"BarChart","data":[{"bin":"50\u201395","count":6},{"bin":"95\u2013140","count":16},{"bin":"140\u2013185","count":10},{"bin":"185\u2013230","count":6},{"bin":"230\u2013275","count":2},{"bin":"275\u2013320","count":2},{"bin":"320\u2013365","count":2},{"bin":"365\u2013410","count":2},{"bin":"410\u2013455","count":2},{"bin":"455\u2013500","count":2}],"series":[{"dataKey":"count"}],"xAxis":"bin","height":300,"showTooltip":true,"animate":true,"showLegend":true,"showGrid":true,"barRadius":4}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgSGlzdG9ncmFtCgpyZXNwb25zZV90aW1lcyA9IFsKICAgIDUwLCA2NSwgNzUsIDgwLCA4NSwgOTAsIDk1LCA5OCwKICAgIDEwMCwgMTA1LCAxMDgsIDExMCwgMTEyLCAxMTUsIDExOCwgMTIwLCAxMjIsIDEyNSwKICAgIDEyOCwgMTMwLCAxMzIsIDEzNSwgMTQwLCAxNDUsIDE0OCwgMTUwLCAxNTUsIDE2MCwKICAgIDE2NSwgMTcwLCAxNzUsIDE4MCwgMTg1LCAxOTAsIDE5NSwgMjAwLCAyMTAsIDIyMCwKICAgIDIzMCwgMjUwLCAyNzUsIDMwMCwgMzIwLCAzNTAsIDM4MCwgNDAwLCA0MjUsIDQ1MCwKICAgIDQ3NSwgNTAwLApdCgpIaXN0b2dyYW0odmFsdWVzPXJlc3BvbnNlX3RpbWVzKQo">
  <CodeGroup>
    ```python Python icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import Histogram

    response_times = [
        50, 65, 75, 80, 85, 90, 95, 98,
        100, 105, 108, 110, 112, 115, 118, 120, 122, 125,
        128, 130, 132, 135, 140, 145, 148, 150, 155, 160,
        165, 170, 175, 180, 185, 190, 195, 200, 210, 220,
        230, 250, 275, 300, 320, 350, 380, 400, 425, 450,
        475, 500,
    ]

    Histogram(values=response_times)
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "type": "BarChart",
        "data": [
          {"bin": "50\u201395", "count": 6},
          {"bin": "95\u2013140", "count": 16},
          {"bin": "140\u2013185", "count": 10},
          {"bin": "185\u2013230", "count": 6},
          {"bin": "230\u2013275", "count": 2},
          {"bin": "275\u2013320", "count": 2},
          {"bin": "320\u2013365", "count": 2},
          {"bin": "365\u2013410", "count": 2},
          {"bin": "410\u2013455", "count": 2},
          {"bin": "455\u2013500", "count": 2}
        ],
        "series": [{"dataKey": "count"}],
        "xAxis": "bin",
        "height": 300,
        "showTooltip": true,
        "animate": true,
        "showLegend": true,
        "showGrid": true,
        "barRadius": 4
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

## Custom Bin Count

Set `bins` to control how many equal-width buckets the data is split into. Fewer bins give a coarser, smoother view of the distribution that emphasizes the overall shape. More bins reveal finer detail -- individual peaks and valleys -- but can make the chart noisy if your dataset is small. A good rule of thumb: start with the default 10 and adjust based on what story the data is telling.

<ComponentPreview json={{"view":{"type":"BarChart","data":[{"bin":"50\u2013140","count":22},{"bin":"140\u2013230","count":16},{"bin":"230\u2013320","count":4},{"bin":"320\u2013410","count":4},{"bin":"410\u2013500","count":4}],"series":[{"dataKey":"count"}],"xAxis":"bin","height":300,"showTooltip":true,"animate":true,"showLegend":true,"showGrid":true,"barRadius":4}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgSGlzdG9ncmFtCgpIaXN0b2dyYW0odmFsdWVzPXJlc3BvbnNlX3RpbWVzLCBiaW5zPTUpCg">
  <CodeGroup>
    ```python Python {3} icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import Histogram

    Histogram(values=response_times, bins=5)
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "type": "BarChart",
        "data": [
          {"bin": "50\u2013140", "count": 22},
          {"bin": "140\u2013230", "count": 16},
          {"bin": "230\u2013320", "count": 4},
          {"bin": "320\u2013410", "count": 4},
          {"bin": "410\u2013500", "count": 4}
        ],
        "series": [{"dataKey": "count"}],
        "xAxis": "bin",
        "height": 300,
        "showTooltip": true,
        "animate": true,
        "showLegend": true,
        "showGrid": true,
        "barRadius": 4
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

## Explicit Bin Edges

When you need precise control over where bins start and end, pass `bin_edges` instead. The list defines the boundaries between bins -- three edges produce two bins, four edges produce three, and so on. Values outside the edge range are excluded.

This is useful when your domain has meaningful thresholds. For API response times, you might define bins around SLA boundaries: "under 100ms" (fast), "100-200ms" (acceptable), "200-300ms" (slow), and "over 300ms" (investigate). The unequal bin widths tell a story that equal-width bins would miss.

<ComponentPreview json={{"view":{"type":"BarChart","data":[{"bin":"50\u2013100","count":8},{"bin":"100\u2013150","count":17},{"bin":"150\u2013200","count":10},{"bin":"200\u2013300","count":6},{"bin":"300\u2013500","count":9}],"series":[{"dataKey":"count"}],"xAxis":"bin","height":300,"showTooltip":true,"animate":true,"showLegend":true,"showGrid":true,"barRadius":4}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgSGlzdG9ncmFtCgpIaXN0b2dyYW0oCiAgICB2YWx1ZXM9cmVzcG9uc2VfdGltZXMsCiAgICBiaW5fZWRnZXM9WzUwLCAxMDAsIDE1MCwgMjAwLCAzMDAsIDUwMF0sCikK">
  <CodeGroup>
    ```python Python {3} icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import Histogram

    Histogram(
        values=response_times,
        bin_edges=[50, 100, 150, 200, 300, 500],
    )
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "type": "BarChart",
        "data": [
          {"bin": "50\u2013100", "count": 8},
          {"bin": "100\u2013150", "count": 17},
          {"bin": "150\u2013200", "count": 10},
          {"bin": "200\u2013300", "count": 6},
          {"bin": "300\u2013500", "count": 9}
        ],
        "series": [{"dataKey": "count"}],
        "xAxis": "bin",
        "height": 300,
        "showTooltip": true,
        "animate": true,
        "showLegend": true,
        "showGrid": true,
        "barRadius": 4
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

## Styled

Customize the bar color with any CSS color string and adjust the chart height. The `color` parameter applies to all bars uniformly -- since histograms represent a single variable's distribution, one color is the norm.

<ComponentPreview json={{"view":{"type":"BarChart","data":[{"bin":"50\u2013125","count":17},{"bin":"125\u2013200","count":18},{"bin":"200\u2013275","count":5},{"bin":"275\u2013350","count":3},{"bin":"350\u2013425","count":3},{"bin":"425\u2013500","count":4}],"series":[{"dataKey":"count","color":"#6d28d9"}],"xAxis":"bin","height":250,"showTooltip":true,"animate":true,"showLegend":true,"showGrid":true,"barRadius":4}}} playground="ZnJvbSBwcmVmYWJfdWkuY29tcG9uZW50cyBpbXBvcnQgSGlzdG9ncmFtCgpIaXN0b2dyYW0oCiAgICB2YWx1ZXM9cmVzcG9uc2VfdGltZXMsCiAgICBiaW5zPTYsCiAgICBjb2xvcj0iIzZkMjhkOSIsCiAgICBoZWlnaHQ9MjUwLAopCg">
  <CodeGroup>
    ```python Python {3-5} icon="python" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    from prefab_ui.components import Histogram

    Histogram(
        values=response_times,
        bins=6,
        color="#6d28d9",
        height=250,
    )
    ```

    ```json Protocol icon="brackets-curly" theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
    {
      "view": {
        "type": "BarChart",
        "data": [
          {"bin": "50\u2013125", "count": 17},
          {"bin": "125\u2013200", "count": 18},
          {"bin": "200\u2013275", "count": 5},
          {"bin": "275\u2013350", "count": 3},
          {"bin": "350\u2013425", "count": 3},
          {"bin": "425\u2013500", "count": 4}
        ],
        "series": [{"dataKey": "count", "color": "#6d28d9"}],
        "xAxis": "bin",
        "height": 250,
        "showTooltip": true,
        "animate": true,
        "showLegend": true,
        "showGrid": true,
        "barRadius": 4
      }
    }
    ```
  </CodeGroup>
</ComponentPreview>

## API Reference

<Card icon="code" title="Histogram Parameters">
  <ParamField body="values" type="list[int | float]" required>
    Raw numeric values to bin.
  </ParamField>

  <ParamField body="bins" type="int" default="10">
    Number of equal-width bins. Ignored when `bin_edges` is set.
  </ParamField>

  <ParamField body="bin_edges" type="list[float] | None" default="None">
    Explicit bin boundaries. Overrides `bins` when provided.
  </ParamField>

  <ParamField body="color" type="str | None" default="None">
    Bar fill color as a CSS color string.
  </ParamField>

  <ParamField body="height" type="int" default="300">
    Chart height in pixels.
  </ParamField>

  <ParamField body="bar_radius" type="int" default="4">
    Corner radius of each bar in pixels.
  </ParamField>

  <ParamField body="show_tooltip" type="bool" default="True">
    Show tooltips on hover.
  </ParamField>

  <ParamField body="show_legend" type="bool" default="False">
    Show a legend below the chart.
  </ParamField>

  <ParamField body="show_grid" type="bool" default="True">
    Show horizontal grid lines.
  </ParamField>

  <ParamField body="css_class" type="str | None" default="None">
    Additional Tailwind CSS classes.
  </ParamField>
</Card>

## Protocol Reference

```json Histogram theme={"theme":{"light":"snazzy-light","dark":"dark-plus"}}
{
  "type": "BarChart",
  "id?": "string",
  "onMount?": "any",
  "values": "array (required)",
  "bins?": 10,
  "bin_edges?": "number[]",
  "data?": "array",
  "series?": "[ChartSeries]",
  "xAxis?": "string",
  "height?": "300",
  "showTooltip?": true,
  "animate?": true,
  "showLegend?": true,
  "showGrid?": true,
  "color?": "string",
  "barRadius?": 4,
  "cssClass?": "string"
}
```
