Skip to content

Browser SDK (WebAssembly)

The browser SDK compiles the Rust evaluation core to WebAssembly using wasm-bindgen. Flag evaluation runs at near-native speed entirely in the browser — no evaluation round-trips to your server.

Installation

bash
npm install @sidekick-flags/browser
# or
yarn add @sidekick-flags/browser

Quick Start

typescript
import { SidekickBrowserClient } from '@sidekick-flags/browser'

const client = new SidekickBrowserClient({
  serverUrl: 'https://flags.yourcompany.com',
  sdkKey: 'your-sdk-key',
})

// Connect once — downloads all flags via SSE
await client.connect()

// Evaluate locally in the browser (no network)
const enabled = client.isEnabled('dark-mode', currentUserId, {
  plan: userPlan,
  beta: 'true',
})

API Reference

new SidekickBrowserClient(options)

OptionTypeRequiredDescription
serverUrlstringYesBase URL of your Sidekick server
sdkKeystringNoSDK key (sent as Authorization: Bearer)
reconnectDelayMsnumberNoReconnect delay on SSE disconnect (default: 3000)

client.connect(): Promise<void>

Opens an SSE connection, receives all flags, and populates the local WASM store. Resolves when bootstrap is complete. The connection is maintained for real-time updates.

client.isEnabled(flagKey, userKey, attributes): boolean

Synchronous, in-process evaluation using the WASM engine.

ParameterTypeDescription
flagKeystringFlag key to evaluate
userKeystringStable user identifier
attributesRecord<string, string>User attributes for targeting

Returns false for unknown flags.

client.disconnect(): void

Closes the SSE connection.

Usage with React

tsx
// hooks/useFlag.ts
import { useEffect, useState } from 'react'
import { flags } from '../lib/flags' // shared client instance

export function useFlag(
  key: string,
  userId: string,
  attributes: Record<string, string> = {}
): boolean {
  const [enabled, setEnabled] = useState(() =>
    flags.isEnabled(key, userId, attributes)
  )

  useEffect(() => {
    // Re-evaluate when flags update
    const unsubscribe = flags.onChange(() => {
      setEnabled(flags.isEnabled(key, userId, attributes))
    })
    return unsubscribe
  }, [key, userId])

  return enabled
}

// Usage in a component
function CheckoutButton({ user }) {
  const newCheckout = useFlag('new-checkout-flow', user.id, {
    plan: user.plan,
  })

  return newCheckout ? <NewCheckout /> : <LegacyCheckout />
}

Usage with Vue

typescript
// composables/useFlag.ts
import { ref, onMounted, onUnmounted } from 'vue'
import { flags } from '../lib/flags'

export function useFlag(key: string, userId: string, attributes = {}) {
  const enabled = ref(flags.isEnabled(key, userId, attributes))

  let unsubscribe: (() => void) | null = null

  onMounted(() => {
    unsubscribe = flags.onChange(() => {
      enabled.value = flags.isEnabled(key, userId, attributes)
    })
  })

  onUnmounted(() => unsubscribe?.())

  return enabled
}

WASM Loading

The SDK uses dynamic import to load the WASM binary lazily. Bundlers (Vite, webpack) will handle the .wasm asset automatically.

For Vite, no extra configuration is needed. For webpack 5, add to your config:

javascript
// webpack.config.js
module.exports = {
  experiments: {
    asyncWebAssembly: true,
  },
}

Attributes Are Local

User attributes passed to isEnabled() are never sent to the server. The evaluation happens entirely inside the WASM module in the browser. Only the flag definitions (keys, rules, percentages) are downloaded from the server.

Released under the MIT License.