Your application on-chain forever! No servers, no hosting fees, no downtime. Welcome to the decentralized web.
react-onchain makes this a reality. It's a CLI tool that inscribes your entire application on-chain using the BSV blockchain and 1Sat Ordinals. Every file—HTML, CSS, JavaScript, images—becomes an immutable ordinal inscription.
The best part? Most apps deploy for less than a penny. Your React app becomes censorship-resistant, permanently accessible, and truly decentralized—all for the cost of a fraction of a cent.
- Complete On-Chain Deployment: Entire app lives on the blockchain
- Automatic Dependency Resolution: Analyzes your build and inscribes files in the correct order
- Reference Rewriting: Automatically updates all file references to use ordinals content URLs
- Built-in Versioning: Every deployment is versioned with unlimited history tracked on-chain
- Decentralized & Extensible: Open source architecture supports multiple indexer and content providers
- Framework Agnostic: Works with Vite, Create React App, Next.js (static export), or any React build tool
- React Router Support: One-line setup for client-side routing (see below)
- UTXO Chaining: Efficiently chains UTXOs to avoid double-spend errors
- Dry Run Mode: Test deployments without spending satoshis
- Smart Caching: Reuses unchanged files from previous deployments to minimize costs
react-onchain is fully open source and decentralized. Anyone can add support for additional indexers and content providers by implementing the service interfaces in src/lib/service-providers/.
- Pluggable Indexers: Add new blockchain indexing services
- Multiple Content Providers: Support for multiple ordinals content delivery networks
- Service Failover: Automatic failover between available providers
- Community Driven: Contribute new providers via pull requests
See src/lib/service-providers/IndexerService.ts for the base interface and src/lib/service-providers/gorilla-pool/ for a reference implementation.
For users:
No installation needed! Just use npx:
npx react-onchain deployFor developers/contributors:
Clone and build from source:
git clone https://github.com/danwag06/react-onchain.git
cd react-onchain
npm install
npm run buildOr install globally for development:
npm install -g .If your app uses React Router for client-side routing, add this one line to your Router component:
import { BrowserRouter as Router } from 'react-router-dom';
function App() {
return (
<Router basename={(window as any).__REACT_ONCHAIN_BASE__ || '/'}>{/* Your routes */}</Router>
);
}That's it! This single line makes your React Router app work with on-chain deployments AND locally with zero configuration.
- When deployed:
window.__REACT_ONCHAIN_BASE__=/content/{txid}_{vout}(for content providers like ordfs.network) - Locally: Falls back to
'/' - On custom domains: Automatically uses
'/'
Why is this needed? React Router needs to know the base path when your app is deployed to a subpath (like /content/{txid}_{vout}). This one-liner automatically detects and configures it.
# For Vite
npm run build
# For Create React App
npm run build
# For Next.js (static export)
npm run build && npm run export
⚠️ Use an Ordinals-Compatible WalletUse a wallet that supports 1Sat Ordinals (we recommend yours.org) to ensure your inscription UTXOs aren't accidentally spent. Regular wallets may not recognize 1-satoshi ordinal outputs and could spend them as regular funds, destroying your inscriptions permanently.
Simply run the deploy command - the CLI will guide you through an interactive setup:
npx react-onchain deployThe interactive prompts will ask you for:
- Build directory: Automatically detects common directories (dist, build, out, etc.)
- Payment key: Your WIF private key for signing transactions
- Version information: Optional version tag and description for versioning
The destination address is automatically derived from your payment key.
The CLI will output the entry point URL. For example, using ordfs.network as a content provider:
https://app.reactonchain.com/content/<txid>_<vout>
After your first deployment, a .env file is automatically created with your configuration. This means subsequent deployments are even simpler - just run:
npx react-onchain deployThe CLI will:
- Auto-detect your previous build directory
- Load your payment key from
.env - Load versioning configuration from
deployment-manifest.json - Prompt you for the new version tag and description (optional)
All configuration is automatically managed for you!
# Deploy application (interactive prompts)
npx react-onchain deploy
# Inscribe a single file
npx react-onchain inscribe <file>
# Query version history (on-chain)
npx react-onchain version:history [versioningOriginInscription]
# Get version details (on-chain)
npx react-onchain version:info <version> [versioningOriginInscription]
# Get inscription info (on-chain)
npx react-onchain version:summary [versioningOriginInscription]
# View deployment history (local)
npx react-onchain manifest:historyThe recommended way to deploy is using the interactive CLI, which guides you through the setup:
npx react-onchain deployThe CLI will:
- Detect your build directory - Automatically finds common directories (dist, build, out, etc.)
- Load saved configuration - Reuses payment key and settings from
.env - Prompt for missing values - Only asks for information that isn't already configured
- Show deployment preview - Displays configuration before deploying
- Request confirmation - Asks you to confirm before spending satoshis
First deployment example:
? Select build directory: ./dist
? Enter payment private key (WIF): **********************
? Enable versioning? Yes
? Enter version tag (e.g., 1.0.0): 1.0.0
? Enter version description: Initial release
? Enter application name: MyDApp
📋 Deployment Configuration
──────────────────────────────────────
Build directory: ./dist
Fee rate: 100 sats/KB
Version: 1.0.0
Description: Initial release
App name: MyDApp (new versioning inscription)
⚠️ This will inscribe files to the blockchain and spend satoshis.
? Proceed with deployment? Yes
🚀 Deploying to BSV Blockchain...
Subsequent deployments:
npx react-onchain deployThe CLI auto-loads everything from .env and only prompts for the new version information!
For automation or CI/CD pipelines, you can bypass interactive prompts using flags:
| Flag | Alias | Description | Default |
|---|---|---|---|
--build-dir <directory> |
-b |
Build directory to deploy | ./dist |
--payment-key <wif> |
-p |
Payment private key in WIF format (destination auto-derived) | Prompted |
--sats-per-kb <number> |
-s |
Satoshis per KB for fees | 100 |
--dry-run |
Test deployment without broadcasting | false |
|
--version-tag <string> |
Version identifier (e.g., "1.0.0") | Prompted | |
--version-description <string> |
Changelog or release notes | Prompted | |
--app-name <string> |
Application name for new versioning inscription | Prompted | |
--change <address> |
-c |
Change address (optional, for UTXO change outputs) | Auto |
--manifest <file> |
-m |
Output manifest file path | Auto |
--ordinal-content-url <url> |
Ordinal content delivery URL | Auto | |
--ordinal-indexer-url <url> |
Ordinal indexer API URL | Auto |
Automated deployment example:
# First deployment with flags (no prompts)
npx react-onchain deploy \
--build-dir ./dist \
--payment-key L1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z \
--version-tag "1.0.0" \
--version-description "Initial release" \
--app-name "MyDApp"
# Subsequent deployment (config auto-loaded from .env)
npx react-onchain deploy \
--version-tag "1.1.0" \
--version-description "Bug fixes"
# Dry run (test without spending)
npx react-onchain deploy --dry-run
# Custom fee rate
npx react-onchain deploy --sats-per-kb 100Note: When flags are provided, interactive prompts are automatically skipped. This is perfect for CI/CD automation.
The inscribe command allows you to inscribe individual files to the BSV blockchain. Unlike the deploy command which handles entire React applications, inscribe is a lower-level utility for inscribing single files directly.
npx react-onchain inscribe <file> [options]| Flag | Short | Description | Default |
|---|---|---|---|
--payment-key <wif> |
-p |
Payment private key in WIF format | From PAYMENT_KEY in .env |
--protocol <type> |
Protocol: b (B:// protocol) or 1sat (1Sat Ordinals) |
b |
|
--destination <address> |
-d |
Destination address for the inscription | Payment key address |
--sats-per-kb <number> |
-s |
Satoshis per KB for transaction fees | From config (defaults to env) |
--content-type <type> |
-t |
MIME content type | Auto-detected from extension |
Basic usage (with .env configuration):
npx react-onchain inscribe ./image.pngInscribe with all options specified:
npx react-onchain inscribe ./document.pdf \
--payment-key "your-wif-key-here" \
--protocol b \
--destination "1A2B3C..." \
--sats-per-kb 50 \
--content-type "application/pdf"Using 1Sat Ordinals protocol:
npx react-onchain inscribe ./rare-nft.png --protocol 1satInscribe a video file:
npx react-onchain inscribe ./video.mp4 --sats-per-kb 10The command auto-detects content types for 40+ file extensions:
- Web:
.html,.css,.js,.mjs,.json,.webmanifest - Images:
.png,.jpg,.jpeg,.gif,.svg,.webp,.ico,.bmp,.tiff - Fonts:
.woff,.woff2,.ttf,.eot,.otf - Video:
.mp4,.webm,.mov,.avi,.mkv,.m4v,.ogg - Audio:
.mp3,.wav,.m4a,.aac,.flac,.ogg - Documents:
.pdf,.txt,.xml,.csv,.md - Other:
.wasm,.zip,.tar,.gz,.7z
For unknown extensions, it defaults to application/octet-stream.
The command displays:
- File information (name, path, size, content type)
- Protocol and destination details
- Fee rate
- Inscription progress
- Success message with:
- Transaction ID
- Outpoint (txid_vout)
- Content URL (e.g., on ordfs.network)
- Total cost in satoshis
The inscribe command is useful for:
- Inscribing individual assets or files
- Testing inscription functionality
- Creating standalone inscriptions outside of full app deployments
- Inscribing files using the B:// protocol (default) or 1Sat Ordinals
- Quick one-off inscriptions without needing a full project structure
This is a simpler, more direct alternative to the deploy command when you just need to put a single file on-chain.
After successful deployment, you'll see detailed information about inscribed files:
⚡ Inscribing to BSV Blockchain
──────────────────────────────────────────────────────────────────────
✓ assets/react-CHdo91hT.svg → 494c43a6...
✓ vite.svg → 712d1b3c...
✓ assets/index-B7tBotfE.js → 896b0d05...
✓ assets/index-COcDBgFa.css → 58b02b11...
✓ index.html → f16f3780...
✓ versioning-metadata → 7b1c2bc4...
──────────────────────────────────────────────────────────────────────
╔═══════════════════════════════════════════════════════════════════╗
║ Deployment Complete! ║
╚═══════════════════════════════════════════════════════════════════╝
📄 New Inscriptions
──────────────────────────────────────────────────────────────────────
1. index.html 6.03 KB f16f3780...
2. versioning-metadata 1.25 KB 7b1c2bc4...
──────────────────────────────────────────────────────────────────────
SUBTOTAL 7.28 KB 2 files
──────────────────────────────────────────────────────────────────────
📦 Cached Files (Reused)
──────────────────────────────────────────────────────────────────────
1. assets/react-CHdo91hT.svg 4.03 KB 494c43a6...
2. vite.svg 1.46 KB 712d1b3c...
3. assets/index-B7tBotfE.js 223.33 KB 896b0d05...
4. assets/index-COcDBgFa.css 1.35 KB 58b02b11...
──────────────────────────────────────────────────────────────────────
SUBTOTAL 230.17 KB 4 files
──────────────────────────────────────────────────────────────────────
📊 Total
──────────────────────────────────────────────────────────────────────
TOTAL 237.45 KB 6 files
──────────────────────────────────────────────────────────────────────
📊 Deployment Stats
──────────────────────────────────────────────────────────────────────
New files: 2 (7.28 KB)
Cached files: 4
Total files: 6 (237.45 KB)
Inscription cost: ~8 satoshis
Transactions: 2
──────────────────────────────────────────────────────────────────────
📦 Versioning
──────────────────────────────────────────────────────────────────────
Origin Inscription: f852b8a7...
Version: 1.0.1
Version redirect: ✓ Enabled
──────────────────────────────────────────────────────────────────────
✨ Entry Point (example using ordfs.network)
https://ordfs.network/content/f16f3780...
📁 Files Saved
• deployment-manifest.json
• .env (configuration for next deployment)
Notice the "Cached Files (Reused)" section? react-onchain intelligently reuses files from previous deployments when content hasn't changed. This dramatically reduces costs for subsequent deployments - you only pay to inscribe what actually changed!
In the example above:
- New inscriptions: 2 files (7.28 KB) - only index.html and versioning metadata changed
- Cached files: 4 files (230.17 KB) - assets remain unchanged and are reused
- Cost savings: ~97% reduction (inscribed 7.28 KB instead of 237.45 KB)
Point your domain to your deployment using DNS redirects or URL rewrites. Each deployment is permanent and accessible at its unique URL—you control which version users see via DNS.
Key Point: Accessing /content/<origin> directly will automatically redirect to the latest deployed version (no configuration needed).
URL Behavior:
/content/<origin>→ Loads the latest version/content/<origin>?version=1.2.0→ Redirects to version specified
All deployments are automatically versioned with on-chain history tracking. Users can access specific versions via URL parameters, enabling safe rollbacks and version pinning.
- First Deployment: Creates a versioning inscription origin with metadata containing version-to-outpoint mappings
- Subsequent Deployments: Spends the previous inscription and adds new version metadata, creating an unlimited version history chain
- Version Redirect: Automatically injected script queries inscription metadata to resolve version queries
- Always Latest: Pointing to your origin html inscription will intelligently redirect to the latest deployed version of it.
The interactive CLI guides you through deploying your first version:
npx react-onchain deployYou'll be prompted for all necessary information:
? Select build directory: ./dist
? Payment key (WIF format): **********************
? App name (for versioning): MyDApp
? Version tag: 1.0.0
? Version description: Initial release
After deployment, a .env file is automatically created containing all your configuration (payment key, build directory, app name, and versioning inscription). The destination address is automatically derived from your payment key. This file is in .gitignore to protect your private keys.
Or use flags for automation:
npx react-onchain deploy \
--build-dir ./dist \
--payment-key <WIF> \
--app-name "MyDApp" \
--version-tag "1.0.0" \
--version-description "Initial release"After your first deployment, subsequent versions are incredibly simple:
npx react-onchain deployThe CLI will:
- Auto-load all configuration from
.envanddeployment-manifest.json - Prompt you only for the new version information
- Automatically inject version redirect script
Or with flags:
npx react-onchain deploy \
--version-tag "1.1.0" \
--version-description "Added dark mode and bug fixes"Version redirect script is automatically injected starting with the second deployment, enabling ?version= URL parameters.
- Latest via inscription: Access via content providers (e.g.,
https://app.reactonchain.com/content/<ORIGIN>) - always serves latest location - Specific version:
<ENTRY_POINT_URL>?version=1.0.0- redirects to specific version
Point your domain to always serve the latest version via your chosen content provider:
# Example: DNS/CDN redirect to always get latest (using app.reactonchain.com)
https://app.reactonchain.com/content/<ORIGIN>
Or point to the entry point and let users control versions via ?version= parameter.
# View all versions (inscription auto-read from manifest if not provided)
npx react-onchain version:history [INSCRIPTION_ORIGIN]
# Get specific version details (inscription auto-read from manifest if not provided)
npx react-onchain version:info <VERSION> [INSCRIPTION_ORIGIN]
# Get inscription information (inscription auto-read from manifest if not provided)
npx react-onchain version:summary [INSCRIPTION_ORIGIN]Advanced: Version Command Options:
All version commands (version:history, version:info, version:summary) support:
| Flag | Description |
|---|---|
-m, --manifest <file> |
Path to manifest file (default: deployment-manifest.json) |
-l, --limit <number> |
Limit number of versions to display (history only) |
-f, --from-version <version> |
Start displaying from a specific version (history only) |
-a, --all |
Show all versions, ignores limit (history only) |
Note: Versioning inscription origin is auto-loaded from the manifest file. All commands accept an optional [inscription] positional argument to override.
A JSON manifest is generated after deployment:
{
"timestamp": "2025-10-27T02:00:00.000Z",
"entryPoint": "/content/abc123_0",
"files": [
{
"originalPath": "index.html",
"txid": "abc123...",
"vout": 0,
"urlPath": "/content/abc123_0",
"size": 2450
}
],
"totalFiles": 4,
"totalCost": 45678,
"totalSize": 162130,
"transactions": ["abc123...", "def456...", "ghi789...", "jkl012..."]
}The manifest file automatically maintains a complete history of all your deployments. Each new deployment is appended to the history, creating a permanent local record.
View your deployment history:
npx react-onchain manifest:historyThe history includes:
- Deployment number and version
- Timestamp for each deployment
- File counts and sizes
- Total costs across all deployments
- Shared versioning inscription origin (if enabled)
Benefits:
- Complete audit trail of all deployments
- Track costs and sizes over time
- Easy reference to previous deployment details
- Automatic migration from old single-deployment format
Note: The manifest stores complete deployment history locally. This complements the on-chain inscription metadata which has unlimited version history. All deployments remain permanently on-chain.
You can use react-onchain programmatically:
import { deployToChain } from 'react-onchain';
const config = {
buildDir: './dist',
paymentKey: 'your-wif-key', // Destination address is auto-derived from this key
version: '1.0.0',
versionDescription: 'Initial release',
appName: 'MyApp',
satsPerKb: 50,
dryRun: false,
};
const result = await deployToChain(config);
console.log(`Entry point: ${result.entryPointUrl}`);
console.log(`Total files: ${result.inscriptions.length}`);
console.log(`Total size: ${result.totalSize} bytes`);
console.log(`Total cost: ${result.totalCost} satoshis`);
console.log(`Versioning origin: ${result.versioningOriginInscription}`);- HTML:
.html,.htm - CSS:
.css - JavaScript:
.js,.mjs - JSON:
.json - Images:
.png,.jpg,.jpeg,.gif,.svg,.webp,.ico - Fonts:
.woff,.woff2,.ttf,.eot,.otf - Other:
.txt,.xml
Inscription costs depend on:
- Number of files
- Total size of all files
- Fee rate (sats/KB)
Typical costs at 100 sats/KB:
| App Type | Size | Files | Cost (sats) | Cost (USD)* |
|---|---|---|---|---|
| Simple SPA | 200 KB | 5-10 | ~20,000 | ~$0.008 |
| Medium App | 500 KB | 20-30 | ~50,000 | ~$0.02 |
| Large App | 1 MB | 50-100 | ~100,000 | ~$0.04 |
| Complex App | 2 MB | 100+ | ~200,000 | ~$0.08 |
*Based on $40 BSV price
- Enable minification: Ensure your build tool minifies code
- Optimize images: Use WebP format, optimize SVGs
- Remove source maps: Exclude
.mapfiles from deployment - Tree shaking: Remove unused code
- Code splitting: Split large bundles into smaller chunks
- Compress assets: Most bundlers gzip automatically
react-onchain uses a domain-driven modular architecture organized by business capability:
react-onchain/
├── src/
│ ├── core/ # Domain-based business logic
│ │ ├── analysis/ # Build analysis & dependency graphs
│ │ │ ├── analyzer.ts
│ │ │ ├── analyzer.types.ts
│ │ │ └── index.ts
│ │ ├── caching/ # File caching & reuse analysis
│ │ │ ├── analyzer.ts
│ │ │ └── index.ts
│ │ ├── chunking/ # File chunking for large files
│ │ │ ├── chunker.ts
│ │ │ ├── chunking.types.ts
│ │ │ └── index.ts
│ │ ├── html/ # HTML inscription (1-sat ordinal chain)
│ │ │ ├── inscriber.ts
│ │ │ └── index.ts
│ │ ├── inscription/ # Blockchain inscription operations
│ │ │ ├── parallelInscriber.ts
│ │ │ ├── utxoSplitter.ts
│ │ │ ├── inscription.types.ts
│ │ │ ├── utils.ts
│ │ │ └── index.ts
│ │ ├── orchestration/ # Wave-based deployment coordination
│ │ │ ├── orchestrator.ts # High-level orchestration
│ │ │ ├── jobBuilder.ts # Wave job preparation
│ │ │ ├── waveProcessor.ts # Wave execution
│ │ │ ├── orchestration.types.ts
│ │ │ └── index.ts
│ │ ├── rewriting/ # URL rewriting (HTML/CSS/JS)
│ │ │ ├── htmlRewriter.ts
│ │ │ ├── cssRewriter.ts
│ │ │ ├── jsRewriter.ts
│ │ │ ├── jsonRewriter.ts
│ │ │ ├── svgRewriter.ts
│ │ │ ├── utils.ts
│ │ │ ├── templates/
│ │ │ │ ├── versionRedirect.template.js
│ │ │ │ ├── basePathFix.template.js
│ │ │ │ └── webpackPublicPathFix.template.js
│ │ │ └── index.ts
│ │ ├── versioning/ # On-chain version management
│ │ │ ├── versioningHandler.ts # Version metadata management
│ │ │ ├── inscriber.ts # Version inscription operations
│ │ │ ├── versioning.types.ts
│ │ │ ├── utils.ts
│ │ │ └── index.ts
│ │ ├── service-worker/ # Service worker generation
│ │ │ ├── generator.ts # SW code generation
│ │ │ ├── inscriber.ts # SW inscription operations
│ │ │ ├── types.ts
│ │ │ ├── ChunkFetcher.ts
│ │ │ ├── RangeCalculator.ts
│ │ │ ├── StreamAssembler.ts
│ │ │ └── index.ts
│ │ └── utils.ts # Shared core utilities
│ ├── lib/ # Configuration & services
│ │ ├── config.ts # Environment & configuration
│ │ └── service-providers/
│ │ ├── IndexerService.ts # Indexer abstraction
│ │ ├── gorilla-pool/
│ │ │ ├── indexer.ts
│ │ │ └── constants.ts
│ │ └── index.ts
│ ├── cli/ # Command-line interface
│ │ ├── cli.ts # CLI entry point
│ │ ├── utils.ts
│ │ └── commands/
│ │ ├── deploy/
│ │ │ ├── index.ts
│ │ │ ├── input.ts
│ │ │ ├── display.ts
│ │ │ └── progress.ts
│ │ ├── version/
│ │ │ ├── index.ts
│ │ │ └── display.ts
│ │ └── manifest/
│ │ ├── index.ts
│ │ └── display.ts
│ ├── utils/ # Shared utilities
│ │ ├── constants.ts
│ │ ├── errors.ts
│ │ ├── errorLogger.ts
│ │ └── retry.ts
│ ├── tests/ # Test suite
│ │ ├── analyzer.test.ts
│ │ ├── chunker.test.ts
│ │ └── sw-local-test.ts
│ └── index.ts # Public API exports
├── dist/ # Compiled JavaScript
└── package.json
Key Architecture Patterns:
- Domain-Driven Design: Each domain is self-contained with types, logic, and utilities
- Barrel Exports: Clean public APIs via
index.tsfiles in each domain - Type Co-location: Domain-specific types in
.types.tsfiles - Service Abstraction: Pluggable indexer providers via
IndexerServiceinterface - Wave-Based Parallelization: Efficient dependency-aware parallel inscription
- Node.js 18+
- BSV wallet with sufficient funds
- Built React application (from
npm run build)
Ensure your payment address has sufficient BSV for inscriptions.
Point to the correct build directory (usually ./dist or ./build).
- Verify your WIF private key is valid
- Ensure payment address has funds
- Check network connectivity
- Wait a few minutes for content provider indexing
- Verify transactions are confirmed on-chain
- Check console for CORS or network errors
- Try an alternative content provider if available
- Build must contain
index.htmlat the root - External CDN references are not rewritten
- Dynamic imports must use relative paths
- Content providers may have indexing delays
Contributions are welcome! Please follow these guidelines:
This project uses Prettier for consistent code formatting.
Format code before committing:
npm run formatCheck if code is formatted correctly:
npm run format:checkVS Code Setup:
If you're using VS Code, formatting on save is already configured in .vscode/settings.json.
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature-name - Make your changes
- Format your code:
npm run format - Build and test:
npm run build - Commit your changes with a descriptive message
- Push to your fork and submit a pull request
- Use TypeScript for all new code
- Follow existing patterns and conventions
- Add JSDoc comments for public APIs
- Keep functions focused and single-purpose
- Use meaningful variable names
- Provide a clear description of the changes
- Reference any related issues
- Ensure code is formatted with Prettier
- Verify the project builds successfully
- Update documentation if needed
For questions or discussions, please open an issue on GitHub.
ISC
- js-1sat-ord - 1Sat Ordinals library
- @bsv/sdk - BSV TypeScript SDK
- commander - CLI framework
- @inquirer/prompts - Interactive CLI prompts
- chalk - Terminal styling
- ora - Terminal spinners
- axios - HTTP client
- dotenv - Environment configuration