Components / Clinical

Lab results table

Lab values flagged against the reference ranges you pass, with a sparkline of earlier results. Flags read as text and an icon, so they never depend on color alone.

Illustrative values, not patient data
TestResultTrend
Glucose, fastingOct 1, 2026Range: 70–99 mg/dL118 mg/dLHigh
Hemoglobin A1cOct 1, 2026Range: 4.0–5.6 %6.1 %High
SodiumOct 1, 2026Range: 135–145 mmol/L133 mmol/LLow
PotassiumOct 1, 2026Range: 3.5–5.1 mmol/L6.4 mmol/LCritical high
CreatinineOct 1, 2026Range: 0.60–1.20 mg/dL0.90 mg/dLNormal
HDL cholesterolOct 1, 2026Range: ≥ 40 mg/dL52 mg/dLNormalNo earlier results
Urine proteinOct 1, 2026Range: NegativeNegativeNormalNo earlier results

Installation

In a project set up as in Installation, run:

bunx shadcn@4.22.1 add https://organui.com/r/lab-results.json

Or by name: bunx shadcn@4.22.1 add @organui/lab-results.

The source installs into your UI components directory, along with shadcn's table if you do not have it yet. It holds no state and uses no hooks, so it renders in server and client components alike.

View registry item JSON

Usage

import { LabResultsTable, type LabResult } from "@/components/ui/lab-results"

const results: LabResult[] = [
  {
    id: "glucose",
    name: "Glucose, fasting",
    value: 118,
    unit: "mg/dL",
    date: "2026-10-01",
    referenceRange: { low: 70, high: 99, criticalLow: 40, criticalHigh: 400 },
    history: [
      { value: 92, date: "2025-10-06" },
      { value: 104, date: "2026-05-18" },
      { value: 118, date: "2026-10-01" },
    ],
  },
  {
    id: "urine-protein",
    name: "Urine protein",
    value: "Negative",
    flag: "normal",
    referenceRange: { text: "Negative" },
  },
]

export function Results() {
  return <LabResultsTable results={results} caption="Basic metabolic panel" />
}

Reference ranges and flags

Ranges are data, not built in, because they vary by laboratory, age, sex and method. Pass low and high as the inclusive limits of normal, either one alone for a one-sided range, and criticalLow or criticalHigh for values outside which a result is critical. Critical limits are checked first. A numeric value with no limits is shown as not flagged.

Text results, such as "Negative", are never compared with a range. Set referenceRange.text to print the expected result, and flag to show the flag the laboratory reported. A flag you pass always wins over the computed one. The labFlag and formatReferenceRange helpers are exported for use elsewhere, such as a summary of abnormal results.

Every flag is a word and an icon: an up or down arrow for high or low, a warning triangle for critical or abnormal, and a check for normal. Critical flags also use your destructive color and abnormal values are bold. Rows carry a data-flag attribute for your own styling.

Trend

Pass history, including the current result, to draw a sparkline. Points are sorted by date. The shaded band is the reference range, and points outside it are drawn as rings. The sparkline's accessible name describes the trend in words, such as "Glucose, fasting trend, 4 results from Oct 6, 2025 to Oct 1, 2026: 92 to 118 mg/dL, rising". Set showTrend={false} to drop the column.

Dates are ISO 8601 strings and show as UTC dates in US English, so the server and client render the same text. Pass formatDate to use your own locale or time zone. Set decimals on a result to fix its decimal places across the value, range and trend.

Layout

The table sizes to its container rather than the viewport. Below 32rem wide, the reference range moves under the test name so the table fits a phone without scrolling sideways.

A display component, not clinical decision support. It does not convert units, interpret results or choose reference ranges, and the example values are illustrative.