Skip to content

Repository files navigation

Server-Driven UI (SDUI) System

A production-ready Server-Driven UI implementation for React Native with two approaches:

  1. 🎯 Hybrid Approach (Recommended) - Mix native code with server-driven chunks
  2. 📦 Legacy Monolithic Approach - Full app driven by server (for reference)

⚡ Quick Start - Hybrid Approach (New!)

The hybrid approach lets you mix static React Native code with dynamic server-driven content. This is the recommended approach for production apps.

1. Start the Server

cd src/sdui/server
npm install
npm run dev

Server runs on http://localhost:3001

2. Find Your IP Address

React Native can't use localhost, so you need your machine's IP:

# macOS/Linux
ifconfig | grep "inet " | grep -v 127.0.0.1

# Windows
ipconfig

3. Wrap Your App

// App.tsx
import { SDUI } from './src/sdui';

export default function App() {
  return (
    <SDUI.Provider
      config={{
        baseURL: 'http://192.168.1.XXX:3001', // ← Your IP here
        enableCache: true,
        cacheTTL: 1000 * 60 * 5, // 5 minutes
      }}
    >
      <MyExistingApp />
    </SDUI.Provider>
  );
}

4. Use in Your Screens

import { SDUI } from './src/sdui';

function HomeScreen() {
  return (
    <ScrollView>
      {/* Static header - you control */}
      <Header title="Home" />

      {/* Dynamic feed - server controls */}
      <SDUI.Renderer
        path="/home/feed.json"
        priority="high"
        cache={true}
        fallback={<Loading />}
      />

      {/* Static footer - you control */}
      <Footer />
    </ScrollView>
  );
}

That's it! 🎉 See HYBRID_QUICKSTART.md for complete guide.


📁 Project Structure

src/sdui/
├── SDUIApp.tsx                    # Legacy: Full monolithic app
├── SDUIAppWithNavigation.tsx      # Legacy: App with navigation
│
├── context/                       # ⭐ NEW: Hybrid API
│   └── SDUIProvider.tsx           #   Context provider
│
├── core/                          # Core systems
│   ├── ComponentRegistry.ts       #   Component mapping
│   ├── ActionHandler.ts           #   Action processing
│   ├── WebSocketService.ts        #   Real-time updates
│   ├── CacheService.ts            #   Offline caching
│   ├── PayloadValidator.ts        #   Validation
│   ├── PerformanceMonitor.ts      #   Performance tracking
│   ├── RetryHelper.ts             #   Retry logic
│   └── SDUILoader.ts              #   ⭐ NEW: Chunked loading
│
├── components/
│   ├── SDUIRenderer.tsx           #   Component renderer
│   ├── Renderer.tsx               #   ⭐ NEW: Hybrid renderer
│   ├── ErrorBoundary.tsx          #   Error handling
│   ├── FallbackUI.tsx             #   Fallback screens
│   └── builtin/                   #   Built-in components
│       ├── TextComponent.tsx
│       ├── ButtonComponent.tsx
│       ├── ListComponent.tsx
│       ├── HeaderComponent.tsx
│       ├── HeadingComponent.tsx
│       ├── ParagraphComponent.tsx
│       ├── SpacerComponent.tsx
│       ├── ContainerComponent.tsx
│       ├── GridComponent.tsx
│       ├── CardComponent.tsx
│       ├── ImageComponent.tsx
│       └── DividerComponent.tsx
│
├── navigation/                    # Navigation support
│   ├── NavigationRenderer.tsx
│   └── SDUIScreen.tsx
│
├── types/                         # TypeScript definitions
│   ├── index.ts
│   └── navigation.ts
│
├── server/                        # Backend server
│   ├── src/
│   │   ├── server.ts              #   Express + WebSocket + Chunked endpoints
│   │   └── types.ts
│   ├── package.json
│   └── tsconfig.json
│
├── playground/                    # Web-based testing playground
│   ├── index.html
│   ├── index-enhanced.html        #   Monaco editor version
│   ├── playground.js
│   ├── monaco-playground.js
│   ├── templates-complete.js
│   ├── package.json
│   └── vite.config.js
│
├── examples/                      # ⭐ NEW: Hybrid examples
│   ├── HybridApp.tsx              #   Complete app setup
│   ├── HybridDemoScreen.tsx       #   Example screens
│   └── README.md                  #   Examples guide
│
└── docs/                          # Documentation
    ├── HYBRID_QUICKSTART.md       # ⭐ NEW: Hybrid quick start
    ├── HYBRID_APPROACH_SPEC.md    # ⭐ NEW: Hybrid architecture
    ├── ARCHITECTURE_PROBLEMS_AND_SOLUTIONS.md  # ⭐ NEW: Design decisions
    ├── CACHING_IMPLEMENTATION.md
    ├── ERROR_HANDLING_IMPLEMENTATION.md
    ├── PERFORMANCE_IMPLEMENTATION.md
    ├── QUICKSTART.md              #   Legacy quick start
    ├── README.md
    └── ...more docs

🎯 Two Approaches

Hybrid Approach (Recommended) ✅

When to use:

  • Adding SDUI to existing app
  • Want control over navigation/structure
  • Need mix of static + dynamic content
  • Want small, focused updates

Pros:

  • ✅ Small payloads (5-20KB per chunk)
  • ✅ Fast loading (<100ms)
  • ✅ Gradual adoption
  • ✅ Low risk
  • ✅ Easy to remove

Example:

<SDUI.Provider config={...}>
  <MyApp>
    <MyHeader />
    <SDUI.Renderer path="/feed.json" />  {/* Dynamic */}
    <MyFooter />
  </MyApp>
</SDUI.Provider>

Read more: HYBRID_QUICKSTART.md

Legacy Monolithic Approach 📦

When to use:

  • Learning/experimentation
  • Proof of concept
  • Simple demo apps
  • Admin panels

Pros:

  • ✅ Everything server-driven
  • ✅ Good for demos

Cons:

  • ❌ Large payloads (100KB-2MB)
  • ❌ Slower loading
  • ❌ All or nothing

Example:

import SDUIApp from './src/sdui/SDUIApp';
export default SDUIApp;

Read more: QUICKSTART.md


🚀 Hybrid API Reference

Components

<SDUI.Provider>

Wraps your app to enable SDUI functionality.

<SDUI.Provider
  config={{
    baseURL: string;              // Required: API base URL
    enableCache?: boolean;        // Default: true
    cacheTTL?: number;            // Default: 1 hour (ms)
    enableWebSocket?: boolean;    // Default: false
    wsURL?: string;               // Required if WebSocket enabled
    timeout?: number;             // Default: 10000ms
    retryAttempts?: number;       // Default: 3
    onError?: (error, path) => void;
    cacheVersion?: string;
  }}
>
  {children}
</SDUI.Provider>

<SDUI.Renderer>

Renders server-driven content chunks.

<SDUI.Renderer
  path="/home/feed.json"          // Required: JSON endpoint

  // Loading
  priority="high"                 // 'high' | 'normal' | 'low'
  lazy={false}                    // Wait until visible

  // Caching
  cache={true}                    // Enable caching
  cacheTTL={300000}               // Cache TTL (ms)

  // Fallbacks
  fallback={<Loading />}          // Loading state
  errorFallback={<Error />}       // Error state

  // Callbacks
  onLoad={(data) => {}}           // On success
  onError={(error) => {}}         // On error

  // Advanced
  liveUpdate={false}              // WebSocket updates
  props={{}}                      // Props for components
  context={{}}                    // Context for server
/>

Hooks

useSDUI()

Access SDUI context for manual control.

const { loader, cache, config, websocket } = SDUI.useSDUI();

// Manual loading
const data = await loader.load('/path.json', {
  priority: 'high',
  cache: false,
});

// Preloading
await loader.preload('/next-screen.json');
await loader.preloadBatch(['/a.json', '/b.json']);

// Cache management
await loader.clearCache('/path.json');  // Clear specific
await loader.clearCache();              // Clear all
await loader.invalidate('/path.json');  // Invalidate + reload

📱 Available Components (12 Built-in)

All components work with both hybrid and legacy approaches.

Content Components

  • text - Basic text display
  • heading - Heading text (H1-H6)
  • paragraph - Paragraph text
  • image - Image display

Layout Components

  • container - Flex container
  • grid - Grid layout
  • card - Card container
  • spacer - Spacing element
  • divider - Visual divider

Interactive Components

  • button - Pressable button (primary/secondary/text)
  • list - Scrollable list
  • header - App header with title/back button

Example JSON

{
  "components": [
    {
      "id": "heading-1",
      "type": "heading",
      "data": {
        "text": "Welcome!",
        "level": 1
      }
    },
    {
      "id": "button-1",
      "type": "button",
      "data": {
        "title": "Click Me",
        "variant": "primary"
      },
      "actions": [{
        "type": "show_alert",
        "trigger": "onPress",
        "payload": {
          "title": "Hello!",
          "message": "Button clicked"
        }
      }]
    }
  ]
}

🎯 Available Actions (6 Built-in)

1. show_alert

Display native alert dialog.

{
  "type": "show_alert",
  "payload": {
    "title": "Hello",
    "message": "World"
  }
}

2. navigate

Navigate to screen.

{
  "type": "navigate",
  "payload": {
    "screen": "Details",
    "params": { "id": 123 }
  }
}

3. fetch_data

Fetch data from API.

{
  "type": "fetch_data",
  "payload": {
    "endpoint": "/api/items",
    "onSuccess": {
      "type": "update_component",
      "componentId": "list-1"
    }
  }
}

4. submit_form

Submit form data.

{
  "type": "submit_form",
  "payload": {
    "formData": { ... }
  }
}

5. update_component

Update component dynamically.

{
  "type": "update_component",
  "payload": {
    "componentId": "text-1",
    "updates": { ... }
  }
}

6. open_url

Open external URL.

{
  "type": "open_url",
  "payload": {
    "url": "https://example.com"
  }
}

🌐 Server Endpoints

Hybrid Chunked Endpoints (Recommended)

// server/src/server.ts

app.post('/home/feed.json', (req, res) => {
  const { context } = req.body;

  res.json({
    components: [
      { id: 'heading-1', type: 'heading', data: { ... } },
      { id: 'button-1', type: 'button', data: { ... } },
    ],
    metadata: {
      version: '1.0.0',
      timestamp: new Date().toISOString(),
    },
  });
});

Demo endpoints included:

  • POST /home/feed.json - Home feed
  • POST /profile/actions.json - Profile actions
  • POST /marketing/banner.json - Marketing banner (A/B test)

Legacy Endpoints

app.post('/api/sdui/screen', (req, res) => {
  const { screenId, context } = req.body;
  // Returns full screen payload
});

app.post('/api/sdui/action', (req, res) => {
  const { action, context } = req.body;
  // Processes actions
});

app.post('/api/sdui/navigation', (req, res) => {
  const { userId, platform } = req.body;
  // Returns navigation config
});

🔧 Adding Custom Components

Same process for both approaches.

1. Create Component

// components/builtin/MyComponent.tsx
import React from 'react';
import { View, Text } from 'react-native';
import { BaseComponentProps } from '../../types';

export const MyComponent: React.FC<BaseComponentProps> = ({ data, style }) => {
  return (
    <View style={style}>
      <Text>{data.message}</Text>
    </View>
  );
};

2. Register Component

// SDUIApp.tsx or your registration file
import { MyComponent } from './components/builtin/MyComponent';

const registry = ComponentRegistry.getInstance();
registry.register('my_component', MyComponent);

3. Use in JSON

{
  "id": "custom-1",
  "type": "my_component",
  "data": {
    "message": "Hello from custom component!"
  }
}

🎮 Using the Playground

Web-based playground for testing SDUI JSON in real-time.

Start Playground

cd src/sdui/playground
npm install
npm run dev

Opens at http://localhost:5173

Features

  • Monaco Editor - Full TypeScript/JSON intellisense
  • Live Preview - See component structure
  • JSON Validation - Real-time schema validation
  • Templates - 10+ pre-built examples
  • Send to App - Push to React Native via WebSocket
  • Stats Dashboard - Payload size, component count

Usage

  1. Select a template or write custom JSON
  2. Edit in Monaco editor
  3. Click "Send to App"
  4. Watch your React Native app update instantly ✨

🐛 Debugging

Console Logging

All modules use prefixed logging:

  • [SDUIProvider] - Provider lifecycle
  • [SDUILoader] - Loading & caching
  • [SDUI.Renderer] - Rendering chunks
  • [ComponentRegistry] - Component registration
  • [ActionHandler] - Action execution
  • [WebSocketService] - WebSocket events
  • [CacheService] - Cache operations
  • [PayloadValidator] - Validation results

Common Issues

Components not rendering

  1. Check component is registered
  2. Verify JSON is valid
  3. Check console for validation errors
  4. Test endpoint in browser

Network errors

  1. Verify server is running (npm run dev)
  2. Check IP address (not localhost for React Native)
  3. Test endpoint: http://192.168.1.XXX:3001/home/feed.json
  4. Check firewall settings

Cache issues

  1. Clear cache: loader.clearCache()
  2. Try cache={false} to bypass
  3. Check TTL hasn't expired
  4. Verify cache version

WebSocket not connecting

  1. Ensure server is running
  2. Check IP address matches
  3. Verify enableWebSocket: true
  4. Check firewall allows port 3001

📚 Documentation

Quick Starts

Architecture

Implementation Details

Examples


🎨 Example Use Cases

Marketing Banner (Hybrid)

<SDUI.Renderer
  path="/marketing/banner.json"
  cache={false}  // Always fresh for A/B tests
  priority="low"
  lazy={true}
/>

Dynamic Feed (Hybrid)

<SDUI.Renderer
  path="/home/feed.json"
  cache={true}
  cacheTTL={300000}  // 5 minutes
  priority="high"
  liveUpdate={true}  // Real-time updates
/>

User-Specific Actions (Hybrid)

<SDUI.Renderer
  path={`/profile/${userId}/actions.json`}
  props={{ userId }}
  cache={true}
/>

🚀 Best Practices

When to Use SDUI

Good for:

  • Marketing content & promos
  • Feature discovery flows
  • A/B testing
  • Dynamic forms
  • Content feeds
  • Frequently changing content

Not good for:

  • Core navigation structure
  • Authentication flows
  • Performance-critical screens
  • Complex business logic
  • Real-time interactions

Chunk Size Guidelines

  • <5KB: Load immediately, high priority
  • 5-10KB: Load on demand, normal priority
  • 10-20KB: Lazy load, normal priority
  • 20-50KB: Lazy load, low priority
  • >50KB: Split into multiple chunks

Caching Strategy

// Stable content - cache 1 hour
<SDUI.Renderer path="/settings.json" cacheTTL={3600000} />

// Dynamic content - cache 5 min
<SDUI.Renderer path="/feed.json" cacheTTL={300000} />

// Live content - no cache
<SDUI.Renderer path="/promo.json" cache={false} />

Priority Guidelines

// Above fold - high
<SDUI.Renderer path="/hero.json" priority="high" />

// Below fold - normal
<SDUI.Renderer path="/content.json" priority="normal" />

// Off screen - low + lazy
<SDUI.Renderer path="/footer.json" priority="low" lazy={true} />

🔄 Migration from Legacy to Hybrid

Before (Legacy)

import SDUIApp from './src/sdui/SDUIApp';
export default SDUIApp;

After (Hybrid)

import { SDUI } from './src/sdui';

export default function App() {
  return (
    <SDUI.Provider config={{ baseURL: '...' }}>
      <NavigationContainer>
        <Tabs>
          <Tab.Screen name="Home" component={HomeScreen} />
        </Tabs>
      </NavigationContainer>
    </SDUI.Provider>
  );
}

function HomeScreen() {
  return (
    <View>
      <Header />
      <SDUI.Renderer path="/home/feed.json" />
      <Footer />
    </View>
  );
}

Benefits

  • ✅ 133x smaller payloads (15KB vs 2MB)
  • ✅ 100x faster parsing (5ms vs 500ms)
  • ✅ Better caching (per-chunk vs all-or-nothing)
  • ✅ Mix static + dynamic
  • ✅ Gradual adoption
  • ✅ Easier to remove

📊 Performance Metrics

Hybrid Approach

  • Initial load: <100ms (from cache)
  • Network load: <500ms (fresh)
  • Chunk size: <20KB average
  • Cache hit rate: >80%
  • Parse time: <10ms

Legacy Approach

  • Initial load: 500ms-2s
  • Network load: 2-5s
  • Payload size: 100KB-2MB
  • Cache hit rate: Variable
  • Parse time: 100-500ms

🛠️ System Features

Core Features

  • ✅ Chunked loading (hybrid)
  • ✅ Smart caching with TTL
  • ✅ Request deduplication
  • ✅ Priority-based loading
  • ✅ Lazy loading
  • ✅ Retry with exponential backoff
  • ✅ Payload validation
  • ✅ Performance monitoring
  • ✅ Error boundaries
  • ✅ WebSocket live updates
  • ✅ Offline support
  • ✅ 12 built-in components
  • ✅ 6 built-in actions
  • ✅ Full TypeScript support

Developer Experience

  • ✅ Web playground with Monaco editor
  • ✅ Complete examples
  • ✅ Comprehensive docs
  • ✅ Console logging
  • ✅ Error fallbacks
  • ✅ Hot reload support

📝 License

MIT


🎯 Next Steps

Getting Started

  1. Read HYBRID_QUICKSTART.md
  2. Start the server
  3. Try the examples
  4. Add one <SDUI.Renderer> to your app
  5. Iterate!

Advanced

  1. Add custom components
  2. Create custom actions
  3. Build your own endpoints
  4. Implement A/B testing
  5. Add WebSocket updates

Future Enhancements

  • Presentation system (modals, bottom sheets)
  • Progressive loading (smart prefetch)
  • Context-aware manifests (auth state)
  • Component-level lazy loading
  • Delta updates

Built with ❤️ using React Native, TypeScript, Express, and WebSockets

Questions? Check the docs or see examples

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages