Transform any blockchain image reference into a working URL 🖼️
Ever tried to display an image stored on the Bitcoin blockchain? Whether it's an NFT, a meme, or any digital asset, blockchain images come in many different formats - and they're not directly displayable in browsers. This library solves that problem by converting any blockchain image reference into a working HTTP URL that you can use anywhere.
Blockchain images are stored as transactions on networks like Bitcoin SV, but browsers can't display b://txid
or ord://txid_0
directly. You need a gateway service to serve the actual image data. This library:
- Normalizes different blockchain image formats into standard URLs
- Works with any gateway service like OrdFS.network, 1Sat Ordinals, or your own
- Handles fallbacks gracefully when images fail to load
- Provides React hooks for seamless frontend integration
- Caches results for better performance
Here are some real blockchain images served through OrdFS.network:
Original reference: ord://a54d3af24a03bcc28f6b3f2dd0ad249ee042b2f4b95810ae5184ab617a74b8b9_0
Original reference: ord://9418af73d138af02465b22dddd7913660dae9219cd260c4db83cda7c84713896_0
Original reference: b://cbacb16c3a03729165542f20404827f1ee91bc1f9783089c41c59524ebf75a22
These images are stored permanently on the Bitcoin blockchain and served through gateway services that make them accessible to web browsers.
- 🚀 Blazing Fast - Sub-microsecond parsing and URL generation
- 📦 Zero Dependencies - No external runtime dependencies
- 🔧 Fully Typed - Complete TypeScript support with detailed types
- 🎯 Framework Agnostic - Works with any JavaScript framework
- ⚛️ React Support - Optional React hooks and components
- 💾 Smart Caching - Built-in performance optimization
- 🔌 Extensible - Custom protocol handlers support
- 🛡️ Secure - Input validation and XSS protection
npm install bitcoin-image
# or
yarn add bitcoin-image
# or
bun add bitcoin-image
import { ImageProtocols } from 'bitcoin-image';
const imageProtocols = new ImageProtocols();
// Convert blockchain URL to display URL
const displayUrl = await imageProtocols.getDisplayUrl('b://txid_0');
// Returns: https://ordfs.network/txid_0
// Parse URL to get components
const parsed = imageProtocols.parse('ord://txid_2');
// Returns: { protocol: 'ord', txid: 'txid', vout: 2, isValid: true }
// Native format with leading slash
const parsed2 = imageProtocols.parse('/8a6c89c47fb4f06a...e78ed1f2_0');
// Returns: { protocol: 'native', txid: '8a6c89c4...', vout: 0, isValid: true }
// ORDFS content path format
const parsed3 = imageProtocols.parse('/content/8a6c89c47fb4f06a...e78ed1f2');
// Returns: { protocol: 'native', txid: '8a6c89c4...', isValid: true }
// Bitcoin-style outpoint notation
const parsed4 = imageProtocols.parse('8a6c89c47fb4f06a...e78ed1f2o0');
// Returns: { protocol: 'native', txid: '8a6c89c4...', vout: 0, isValid: true }
Protocol | Format | Example |
---|---|---|
Bitcoin Files | b://txid or b://txid_vout |
b://8a6c...1f2_0 |
Ordinals | ord://txid or ord://txid_vout |
ord://8a6c...1f2_0 |
BitFS | bitfs://txid.out.vout |
bitfs://8a6c...1f2.out.0.3 |
IPFS | ipfs://hash |
ipfs://QmX... |
Data URI | data:mime;base64,... |
data:image/png;base64,... |
HTTP(S) | https://... |
https://example.com/image.jpg |
Native | txid , txid_vout , txid.vout , txido0 , txidi0 , /txid , /content/txid |
8a6c...1f2_0 or 8a6c...1f2.0 or 8a6c...1f2o0 |
Parse any supported URL format into its components.
const parsed = imageProtocols.parse('b://txid_0');
// {
// protocol: 'b',
// txid: 'txid',
// vout: 0,
// isValid: true,
// original: 'b://txid_0'
// }
Convert any supported format to a display-ready URL.
const displayUrl = await imageProtocols.getDisplayUrl('ord://txid', {
fallback: '/default.png',
timeout: 5000
});
Batch convert multiple URLs efficiently.
const urls = ['b://txid1', 'ord://txid2'];
const results = await imageProtocols.getDisplayUrls(urls);
// Map { 'b://txid1' => 'https://...', 'ord://txid2' => 'https://...' }
Validate a URL without converting it.
const validation = imageProtocols.validate('b://invalid');
// { isValid: false, error: 'Invalid transaction ID' }
const imageProtocols = new ImageProtocols({
// Custom protocol handlers
handlers: {
b: (parsed) => `https://mygateway.com/${parsed.txid}_${parsed.vout || 0}`
},
// Default fallback image
fallbackImage: '/placeholder.png',
// Enable/disable caching
cacheEnabled: true,
cacheTTL: 3600, // seconds
// Validate transaction IDs
validateTxid: true
});
Register custom handlers for any protocol:
imageProtocols.registerHandler('custom', (parsed) => {
return `https://custom-gateway.com/${parsed.txid}`;
});
Optional React hooks and components for seamless integration:
import { useBlockchainImage, BlockchainImage } from 'bitcoin-image/react';
// Hook usage
function MyComponent({ imageUrl }) {
const { displayUrl, loading, error } = useBlockchainImage(imageUrl);
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return <img src={displayUrl} alt="Blockchain image" />;
}
// Component usage
function Gallery({ images }) {
return (
<div>
{images.map(url => (
<BlockchainImage
key={url}
src={url}
fallback="/placeholder.png"
lazy
className="gallery-image"
/>
))}
</div>
);
}
useBlockchainImage(url, options)
- Load a single imageuseBlockchainImages(urls, options)
- Load multiple imagesuseLazyBlockchainImage(url, options)
- Lazy load with IntersectionObserver
Benchmarked on MacBook Pro M1:
- Parsing: ~0.2-0.5μs per URL
- URL Generation: ~0.1-0.3μs per URL (cached)
- Cache Performance: 15x speedup
- Memory: < 1KB per instance
- Bundle Size: ~10KB minified + gzipped
const urls = Array.from({ length: 1000 }, (_, i) => `b://txid${i}`);
const results = await imageProtocols.getDisplayUrls(urls);
// Processes efficiently with concurrency control
import { validators, utils } from 'bitcoin-image';
// Validate transaction ID
const isValid = validators.isValidTxid('your-txid');
// Security check
const security = utils.validateImageSecurity(url);
if (!security.isValid) {
console.error(security.reason);
}
import { utils } from 'bitcoin-image';
// Extract metadata
const metadata = utils.extractMetadata('bitfs://txid.out.0.3');
// { protocol: 'bitfs', txid: 'txid', vout: 0, chunk: 3 }
// Check if URL is likely an image
const isImage = utils.isLikelyImage(url);
// Generate responsive srcset
const srcset = utils.generateSrcSet(displayUrl, [320, 640, 1280]);
See the examples directory for:
- Command-line tool
- React application
- Next.js integration
- Browser demo
- Node.js scripts
Works in all modern browsers and Node.js 14+. For older browsers, you may need to polyfill:
Promise
Map
URL
Contributions are welcome! Please read our Contributing Guide for details.
MIT © [Open Protocol Labs]
Built for the BSV blockchain ecosystem. Special thanks to the 1Sat Ordinals community.