Skip to content

deckyfx/dioxus-react-bridge

Repository files navigation

dioxus-react-bridge

React hooks and components for seamless IPC communication with Dioxus Rust backend.

npm version License: MIT

Features

  • HTTP-like IPC API - Request/response pattern with methods, URLs, headers, bodies
  • Event Streaming - Real-time events from Rust → React with pub/sub pattern
  • Streaming Tasks - Progress tracking and chunked data transfer for long-running operations
  • Zero Runtime Dependencies - Lightweight EventEmitter replaces RxJS (~50-80KB saved)
  • TypeScript First - Full type safety with comprehensive type definitions
  • Global Type Definitions - Complete types for all Dioxus-injected APIs (window.dioxusBridge, window.ipc, etc.)
  • Build Utility - Configurable build tool with Tailwind support, watch mode, and auto-copy to Dioxus assets
  • React Hooks - Idiomatic React patterns with hooks for all IPC operations
  • Platform Agnostic - Works on desktop (webview), web (WASM), and mobile

Installation

# Using bun
bun add dioxus-react-bridge react react-dom

# Using npm
npm install dioxus-react-bridge react react-dom

# Using yarn
yarn add dioxus-react-bridge react react-dom

Quick Start

1. TypeScript Setup (Recommended)

Import global type definitions in your entry file for full TypeScript support of Dioxus-injected APIs:

// src/index.tsx or src/main.tsx
import '@deckyfx/dioxus-react-bridge/global';

This provides complete type definitions for:

  • window.dioxusBridge - Main IPC bridge
  • window.ipc - Low-level IPC channel
  • window.interpreter - Dioxus virtual DOM interpreter
  • window.showDXToast(), window.scheduleDXToast(), window.closeDXToast() - Toast notifications

Alternative: Add to tsconfig.json

{
  "compilerOptions": {
    "types": ["@deckyfx/dioxus-react-bridge/global"]
  }
}

2. Wrap Your App

import { IPCReady, RustIPCProvider } from '@deckyfx/dioxus-react-bridge';
import '@deckyfx/dioxus-react-bridge/global'; // TypeScript types

function App() {
  return (
    <IPCReady>
      <RustIPCProvider>
        <MyApp />
      </RustIPCProvider>
    </IPCReady>
  );
}

3. Make IPC Calls

import { useRustIPC } from '@deckyfx/dioxus-react-bridge';

function Calculator() {
  const { fetch } = useRustIPC();
  const [result, setResult] = useState(null);

  const calculateFibonacci = async () => {
    const response = await fetch('ipc://calculator/fibonacci?number=10');
    setResult(response.body.result);
  };

  return (
    <div>
      <button onClick={calculateFibonacci}>Calculate</button>
      {result && <div>Result: {result}</div>}
    </div>
  );
}

4. Listen to Rust Events

import { useRustEventListener } from '@deckyfx/dioxus-react-bridge';

function HeartbeatMonitor() {
  const heartbeat = useRustEventListener<{ count: number }>('rust:heartbeat');

  return <div>Heartbeat: {heartbeat?.count || 'Waiting...'}</div>;
}

5. Direct Access to Dioxus APIs (with Types!)

// Thanks to global types, you get full IntelliSense and type checking
async function loadAsset() {
  // window.dioxusBridge is fully typed
  const response = await window.dioxusBridge.fetch('ipc://assets/image');
  console.log(response.body);
}

function showNotification() {
  // Toast functions are fully typed
  window.showDXToast?.('Success', 'Operation completed!', 'success', 3000);
}

Complete Workflow Guide

Step 1: Create Dioxus App

dx new my-app
cd my-app

Step 2: Add Rust Crates

Add to Cargo.toml:

[dependencies]
dioxus = "0.7.0"
dioxus-ipc-bridge = { path = "../dioxus-ipc-bridge" }
dioxus-ipc-bridge-macros = { path = "../dioxus-ipc-bridge-macros" }
dioxus-react-integration = { path = "../dioxus-react-integration" }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

Step 3: Set Up Rust Side

use dioxus::prelude::*;
use dioxus_ipc_bridge::prelude::*;

fn main() {
    // Initialize IPC bridge
    let bridge = IpcBridge::builder()
        .timeout(std::time::Duration::from_secs(30))
        .build();

    bridge.initialize();

    // Launch Dioxus app
    dioxus::launch(app);
}

fn app() -> Element {
    rsx! {
        // Your Dioxus UI
        ReactContainer {
            bundle: asset!("/assets/react/bundle.js"),
            mount_id: "react-root"
        }
    }
}

Step 4: Create React App

# Initialize bun project
mkdir react-app && cd react-app
bun init

# Install dependencies
bun add react react-dom dioxus-react-bridge

# Install dev dependencies
bun add -d @types/react @types/react-dom typescript

Step 5: Write React App

Create src/index.tsx:

import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
import { IPCReady, RustIPCProvider, useRustIPC } from '@deckyfx/dioxus-react-bridge';
import '@deckyfx/dioxus-react-bridge/global'; // Import global types

function App() {
  const { fetch } = useRustIPC();
  const [result, setResult] = useState(null);

  const handleClick = async () => {
    const response = await fetch('ipc://endpoint');
    setResult(response.body);
  };

  return (
    <div>
      <button onClick={handleClick}>Call Rust</button>
      {result && <div>{JSON.stringify(result)}</div>}
    </div>
  );
}

const root = document.getElementById('react-root');
if (root) {
  createRoot(root).render(
    <IPCReady>
      <RustIPCProvider>
        <App />
      </RustIPCProvider>
    </IPCReady>
  );
}

Step 6: Build React Bundle

Create build.ts using the built-in build utility:

import { buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';

await buildForDioxus({
  // Entry point for your React app
  entrypoint: './src/index.tsx',

  // Where to output the bundle
  outdir: './dist',

  // Path to your Dioxus app's assets directory
  dioxusAssetsPath: '../my-dioxus-app/assets/react',

  // Minify the output
  minify: true,

  // Optional: Tailwind CSS processing
  tailwind: {
    input: './src/App.css',
    output: './src/App.processed.css',
  },

  // Optional: Verbose logging
  verbose: true,
});

Minimal Configuration:

import { buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';

// Just specify where to copy the bundle
await buildForDioxus({
  dioxusAssetsPath: '../my-dioxus-app/assets/react'
});

Run build:

bun run build.ts

Development Watch Mode:

Create watch.ts for automatic rebuilding during development:

import { watchForDioxus } from '@deckyfx/dioxus-react-bridge/build';

await watchForDioxus({
  dioxusAssetsPath: '../my-dioxus-app/assets/react',
  verbose: true
});

Run watch mode:

bun run watch.ts

Step 7: Run Your App

The build utility automatically copies the bundle to your Dioxus assets directory (if dioxusAssetsPath is specified).

If you didn't use dioxusAssetsPath, manually copy:

cp react-app/dist/bundle.js ../my-app/assets/react/bundle.js

Step 8: Start Dioxus

cd my-app
dx serve

API Reference

Build Utility

The package includes a configurable build utility to simplify bundling React apps for Dioxus.

buildForDioxus(config?)

Main build function that handles Tailwind CSS processing, bundling, and copying to Dioxus assets.

import { buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';

await buildForDioxus({
  entrypoint: './src/index.tsx',
  outdir: './dist',
  outputFilename: 'bundle.js',
  dioxusAssetsPath: '../my-dioxus-app/assets/react',
  minify: true,
  tailwind: {
    input: './src/App.css',
    output: './src/App.processed.css',
    options: '--minify'
  },
  loaders: {
    '.png': 'file',
    '.svg': 'file'
  },
  verbose: true
});

Configuration Options:

Option Type Default Description
entrypoint string './src/index.tsx' Entry point for your React app
outdir string './dist' Output directory
outputFilename string 'bundle.js' Output filename
dioxusAssetsPath string undefined Path to Dioxus assets directory
minify boolean true Enable minification
target string 'browser' Build target ('browser', 'bun', 'node')
tailwind object undefined Tailwind CSS configuration
tailwind.input string './src/App.css' Tailwind input file
tailwind.output string './src/App.processed.css' Tailwind output file
tailwind.options string '--minify' Tailwind CLI options
loaders object See defaults Custom file loaders
external string[] [] Packages to exclude from bundle
define object {} Environment variables to define
customBunConfig object {} Custom Bun build configuration
verbose boolean false Enable verbose logging

Default Loaders:

  • .css'text' (inlined as string)
  • .svg, .png, .jpg, .jpeg, .gif, .webp'file' (base64 encoded)
  • .woff, .woff2, .ttf, .otf, .eot'file' (base64 encoded)

watchForDioxus(config?)

Watch mode for automatic rebuilding during development.

import { watchForDioxus } from '@deckyfx/dioxus-react-bridge/build';

await watchForDioxus({
  dioxusAssetsPath: '../my-dioxus-app/assets/react',
  verbose: true
});

Features:

  • Watches all files in src/ directory recursively
  • Automatic rebuild on file changes
  • Ignores node_modules and processed files
  • Initial build on start

createBuildConfig(config)

Create a reusable build configuration with defaults applied.

import { createBuildConfig, buildForDioxus } from '@deckyfx/dioxus-react-bridge/build';

const myConfig = createBuildConfig({
  dioxusAssetsPath: '../my-app/assets/react',
  minify: process.env.NODE_ENV === 'production',
  verbose: process.env.DEBUG === 'true'
});

await buildForDioxus(myConfig);

Components

<IPCReady>

Suspense-like component that waits for window.dioxusBridge to be initialized.

<IPCReady
  fallback={<Spinner />}
  onReady={() => console.log('IPC ready!')}
  timeout={10000}
>
  <App />
</IPCReady>

Props:

  • fallback?: ReactNode - Custom loading UI
  • onReady?: () => void - Callback when IPC is ready
  • timeout?: number - Timeout in ms (default: 10000)

<RustIPCProvider>

Context provider for IPC functionality.

<RustIPCProvider>
  <App />
</RustIPCProvider>

Hooks

useRustIPC()

Main hook for HTTP-like IPC requests.

const { fetch, isReady } = useRustIPC();

// GET request
const response = await fetch('ipc://endpoint');

// POST request
const response = await fetch('ipc://endpoint', {
  method: 'POST',
  body: { key: 'value' }
});

Returns:

  • fetch<T>(url, options?) - HTTP-like fetch function
  • isReady: boolean - Whether IPC is ready

useRustEventListener(channel)

Listen to events from Rust.

const heartbeat = useRustEventListener<{ count: number }>('rust:heartbeat');

Parameters:

  • channel: string - Event channel name

Returns:

  • T | null - Latest event data

useRustEventEmitter(channel)

Emit events to a channel.

const emit = useRustEventEmitter<{ message: string }>('notification');

emit({ message: 'Hello!' });

Parameters:

  • channel: string - Event channel name

Returns:

  • (data: T) => void - Emit function

useStreamingTask(options)

Manage long-running streaming tasks with progress tracking.

const { start, progress, result, isRunning, cancel } = useStreamingTask({
  onProgress: (p) => console.log(`Progress: ${p.percent}%`),
  onComplete: (result) => console.log('Done!', result),
});

// Start task
await start('ipc://process/video', {
  method: 'POST',
  body: { videoId: '123' }
});

Options:

  • onProgress?: (progress) => void
  • onChunk?: (chunk) => void
  • onComplete?: (result) => void
  • onError?: (error) => void
  • autoReassemble?: boolean

Returns:

  • taskId: string | null
  • isRunning: boolean
  • progress: StreamingProgress | null
  • chunks: StreamingChunk[]
  • result: T | null
  • error: Error | null
  • start: (url, options) => Promise<void>
  • cancel: () => void
  • reset: () => void

Type Exports

Main Types

import type {
  IpcFetchOptions,
  IpcResponse,
  IpcErrorResponse,
  StreamingProgress,
  StreamingChunk,
  StreamingTaskOptions,
  StreamingTaskState,
  DioxusBridge,
  IPCBridgeInstance,
} from '@deckyfx/dioxus-react-bridge';

Global Type Definitions

Import global type definitions for complete TypeScript support of Dioxus-injected APIs:

// Import in your entry file
import '@deckyfx/dioxus-react-bridge/global';

// Now you get full type safety for:
window.dioxusBridge.fetch('ipc://endpoint');  // ✓ Fully typed
window.ipc.postMessage(JSON.stringify({...})); // ✓ Fully typed
window.showDXToast?.('Title', 'Message', 'success', 3000); // ✓ Fully typed
window.interpreter?.scrollTo(id, options);    // ✓ Fully typed (internal API)

Available Global Types:

  • window.dioxusBridge - Main IPC bridge with HTTP-like fetch API

    • fetch<T>(url, options?) - Make IPC requests
    • rustEmit(channel, data) - Emit events to React (called by Rust)
    • IPCBridge - Event system for pub/sub
    • ipc.send(data) - Low-level send
    • ipc.hasIPCBridge() - Check for event system
  • window.ipc - Low-level IPC channel

    • postMessage(message) - Send raw IPC message to Rust
  • window.interpreter - Dioxus virtual DOM interpreter (internal, advanced use only)

    • DOM manipulation: scroll, scrollTo, setFocus, getClientRect
    • Scroll getters: getScrollHeight, getScrollLeft, getScrollTop, getScrollWidth
    • Edit management: enqueueBytes, flushQueuedBytes, rafEdits
    • Drag & drop: handleWindowsDragDrop, handleWindowsDragOver, handleWindowsDragLeave
  • window.showDXToast() - Show toast notification

  • window.scheduleDXToast() - Schedule toast after reload

  • window.closeDXToast() - Close current toast

Example with Full Type Safety:

import '@deckyfx/dioxus-react-bridge/global';

// All of these have full IntelliSense and type checking!
async function example() {
  // Main IPC bridge
  const response = await window.dioxusBridge.fetch<{ result: number }>(
    'ipc://calculator/fibonacci?number=10'
  );
  console.log(response.body.result); // ✓ TypeScript knows this is a number

  // Toast notifications
  window.showDXToast?.('Success', 'Calculation complete!', 'success', 3000);

  // Event system
  window.dioxusBridge.IPCBridge?.on('rust:heartbeat').subscribe({
    next: (data) => console.log(data)
  });
}

Architecture

┌─────────────────────────────────────┐
│   React App (Your Code)             │
│   - useRustIPC(), useRustEventListener │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│   dioxus-react-bridge (This Package)│
│   - HTTP-like fetch wrapper         │
│   - Event system (EventBridge)      │
│   - React hooks and components      │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│   window.dioxusBridge (Injected)    │
│   - Native Dioxus IPC channel       │
│   - Injected by Rust before React   │
└──────────────┬──────────────────────┘
               │
┌──────────────▼──────────────────────┐
│   Rust Backend (Dioxus + IPC Bridge)│
│   - Route handlers                  │
│   - Event emission                  │
│   - Business logic                  │
└─────────────────────────────────────┘

Bundle Size

This package has ZERO runtime dependencies except for React peer dependencies.

  • RxJS removed: Saved ~50-80KB (replaced with 2KB EventEmitter)
  • Minified: ~8KB
  • Gzipped: ~3KB

Browser Support

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+

Contributing

Contributions welcome! Please open an issue or PR.

License

MIT © 2025 dioxus-react-bridge contributors

Related Projects

Support

About

A bun lib as client side and companion for dioxus-react-integration

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published