React components for real-time Bitcoin data. Drop them into any React app — no API keys, no configuration required.
npm install bitcoin-widgets| Component | Data source | Default refresh |
|---|---|---|
<BitcoinPrice /> |
blockchain.info | 30s |
<BlockHeight /> |
mempool.space | 30s |
<HalvingCountdown /> |
mempool.space | 60s |
import { BitcoinPrice, BlockHeight, HalvingCountdown } from 'bitcoin-widgets'
export default function App() {
return (
<>
<BitcoinPrice />
<BlockHeight />
<HalvingCountdown />
</>
)
}Displays the current BTC/USD price fetched from blockchain.info.
<BitcoinPrice />
// → ₿ $105,432 (+1.24%)| Prop | Type | Default | Description |
|---|---|---|---|
refreshInterval |
number |
30000 |
Polling interval in milliseconds |
className |
string |
— | CSS class on the root element |
style |
CSSProperties |
— | Inline styles on the root element |
children |
(data, loading, error) => ReactNode |
— | Render prop for custom UI |
Pass a function as children to take full control over what renders during each phase of the data lifecycle. The function receives (data, loading, error) — exactly one of these will be active at a time. This completely replaces the built-in widget UI, so theme and styleOverrides are ignored when children is provided.
<BitcoinPrice>
{(data, loading, error) => {
if (loading) return <Spinner />
if (error) return <p>Unavailable</p>
return <h1>${data.usd.toLocaleString()}</h1>
}}
</BitcoinPrice>Displays the current Bitcoin block height fetched from mempool.space.
<BlockHeight />
// → Block #951,993| Prop | Type | Default | Description |
|---|---|---|---|
refreshInterval |
number |
30000 |
Polling interval in milliseconds |
className |
string |
— | CSS class on the root element |
style |
CSSProperties |
— | Inline styles on the root element |
children |
(data, loading, error) => ReactNode |
— | Render prop for custom UI |
Pass a function as children to take full control over what renders during each phase of the data lifecycle. The function receives (data, loading, error) — exactly one of these will be active at a time. This completely replaces the built-in widget UI, so theme and styleOverrides are ignored when children is provided.
<BlockHeight>
{(data, loading, error) => {
if (loading) return <Spinner />
if (error) return <p>Unavailable</p>
return <p>Chain tip: {data.height.toLocaleString()}</p>
}}
</BlockHeight>Displays blocks remaining until the next Bitcoin halving, with an estimated date. Computed from the current block height — no separate API call.
<HalvingCountdown />
// → 98,007 blocks until halving (~4/17/2028)| Prop | Type | Default | Description |
|---|---|---|---|
refreshInterval |
number |
60000 |
Polling interval in milliseconds |
className |
string |
— | CSS class on the root element |
style |
CSSProperties |
— | Inline styles on the root element |
children |
(data, loading, error) => ReactNode |
— | Render prop for custom UI |
Pass a function as children to take full control over what renders during each phase of the data lifecycle. The function receives (data, loading, error) — exactly one of these will be active at a time. This completely replaces the built-in widget UI, so theme and styleOverrides are ignored when children is provided.
<HalvingCountdown>
{(data, loading, error) => {
if (loading) return <Spinner />
if (error) return <p>Unavailable</p>
return (
<div>
<strong>{data.blocksUntilHalving.toLocaleString()}</strong> blocks to go
<br />
Next halving at block {data.nextHalvingHeight.toLocaleString()}
</div>
)
}}
</HalvingCountdown>{
blocksUntilHalving: number // blocks remaining
nextHalvingHeight: number // absolute block height of next halving
halvingsCount: number // number of halvings that have occurred
estimatedTimestamp: number // estimated Unix ms timestamp (10 min/block average)
timestamp: number // when this data was fetched
}Note: The estimated date is based on a 10-minute average block time and will drift as actual block times vary.
Every widget accepts two style props: theme selects a built-in preset, and styleOverrides surgically overrides individual parts of the widget without replacing the whole theme. Both props are ignored when the children render prop is used.
| Value | Description |
|---|---|
minimal |
Clean, borderless default |
classic |
Dark flip-board aesthetic with gold accents |
tech-dark |
Dark panel with cyan/teal accents |
tech-light |
Light panel with blue accents |
terminal |
Green-on-black terminal look |
<BitcoinPrice theme="terminal" />
<BlockHeight theme="tech-dark" />
<HalvingCountdown theme="classic" />styleOverrides accepts a partial object where each key maps to a named part of the widget. Pass any valid React.CSSProperties value — it merges on top of the active theme.
| Slot | Targets |
|---|---|
container |
Outer wrapper <div> of the widget |
label |
Small text label above the value (e.g. "Bitcoin Price") |
valueRow |
Flex row containing the unit symbol and digit tiles |
unit |
The ₿ or # symbol tile |
digit |
Each individual character tile in the value display |
change |
The 24h % change badge (BitcoinPrice only) |
meta |
Estimated date line below digits (HalvingCountdown only) |
<BitcoinPrice
theme="tech-dark"
styleOverrides={{
container: { background: '#22051e', borderColor: '#57003a' },
label: { color: '#ffbefc' },
unit: { color: '#e87be1' },
digit: { color: '#f71ae8', textShadow: '0 0 8px rgba(247,26,232,0.6)' },
change: { fontWeight: 700 },
}}
/>All three components are tree-shakeable from the main entry point. If your bundler does not support "exports" sub-paths, the barrel import below always works:
// barrel (recommended — modern bundlers tree-shake this correctly)
import { BitcoinPrice } from 'bitcoin-widgets'
// sub-path (maximum isolation)
import { BitcoinPrice } from 'bitcoin-widgets/bitcoin-price'
import { BlockHeight } from 'bitcoin-widgets/block-height'
import { HalvingCountdown } from 'bitcoin-widgets/halving-countdown'npm install react react-domRequires React ≥ 17.
git clone https://github.com/b0rgbart3/bitcoin-widgets.git
cd bitcoin-widgets
npm install
npm run demo # Vite dev server at http://localhost:5173
npm run build # produce dist/
npm run typecheckMIT