npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

growthbook-roku

v1.4.1

Published

Official GrowthBook SDK for Roku/BrightScript applications

Readme

GrowthBook Roku SDK

License: MIT BrightScript Roku Version Production Ready

Official GrowthBook SDK for Roku/BrightScript applications. Add feature flags and A/B testing to your Roku channels with a simple, lightweight SDK.

Current Version: v1.4.1 — View Changelog | Integration Guide | Quick Start

Features

  • 🚀 Lightweight - Core SDK is ~50KB, minimal memory footprint
  • Fast - Feature evaluation in <1ms, optimized for Roku devices
  • 🎯 Full Spec Alignment - 401/401 native BrightScript tests passing (all 8 spec categories + smoke tests)
  • 🧪 A/B Testing - Run experiments with accurate traffic splits and holdout groups
  • 🔄 Consistent Bucketing - Same user always sees same variation (cross-platform)
  • 📊 Analytics Ready - Tracking callbacks, feature usage events, and plugin system
  • 🔒 Encrypted Features - AES-128-CBC decryption for secure feature payloads (Roku OS 9.2+)
  • 📌 Sticky Bucketing - Persistent experiment assignments with in-memory and registry-based storage
  • 🔌 Tracking Plugins - Extensible plugin architecture with built-in data warehouse integration
  • 🔃 On-Demand Refresh - Manual feature refresh via refreshFeatures()
  • 📦 Ropm Support - Easy dependency management via ropm
  • 🎨 No Dependencies - Pure BrightScript, works on all Roku devices (Roku 3+, OS 9.0+)

Quick Start

1. Installation

Manual Installation

Copy GrowthBook.brs from the source/ directory to your channel's source/ folder:

your-roku-channel/
├── source/
│   ├── main.brs
│   └── GrowthBook.brs  ← Add this file
└── manifest

Installation with ropm (Recommended)

If you are using ropm for dependency management:

ropm install growthbook-roku

2. Initialize

' In your main.brs or scene component
function initGrowthBook() as object
    config = {
        apiHost: "https://cdn.growthbook.io",
        clientKey: "sdk-abc123",  ' Your GrowthBook client key
        attributes: {
            id: "user-123",
            deviceType: "roku",
            premium: true
        }
    }
    
    gb = GrowthBook(config)
    
    ' Load features from GrowthBook API
    if gb.init()
        print "GrowthBook ready!"
    end if
    
    return gb
end function

3. Use Feature Flags

' Boolean feature flag
if gb.isOn("new-player-ui") then
    showNewPlayer()
else
    showLegacyPlayer()
end if

' Get feature value with fallback
buttonColor = gb.getFeatureValue("cta-color", "#0000FF")
maxVideos = gb.getFeatureValue("videos-per-page", 12)

' JSON feature configuration
playerConfig = gb.getFeatureValue("player-settings", {
    autoplay: false,
    quality: "HD"
})

Important: Instance Management (SINGLETON PATTERN IS SUGGESTED)

Create one instance and resuse it.

' Initialize ONCE when app starts
m.global.addFields({ gb: invalid })

function InitApp()
    m.global.gb = GrowthBook({
        clientKey: "sdk_YOUR_KEY",
        attributes: { id: GetUserId() }
    })
    m.global.gb.init()
end function

' Reuse the same instance throughout your app
function ShowFeature()
    if m.global.gb.isOn("feature-key") then
        ' Feature logic
    end if
end function

' Update attributes when needed (e.g., user login)
function OnUserLogin(newUserId)
    m.global.gb.setAttributes({ id: newUserId })
    ' Features are now re-evaluated with new user ID
end function

Why This Matters

Creating a new instance for each feature check causes:

| Problem | Impact | |---------|--------| | Redundant API calls | Every init() call fetches features again (expensive) | | Memory waste | Each instance consumes ~150KB of memory | | Inconsistent variations | Different hash seeds mean same user gets different variations | | Poor performance | Network latency on every feature evaluation | | Broken experiments | Inconsistent variation assignment for A/B tests |

Best Practice: Singleton at App Level

' In your app's global initialization
function Main()
    ' Create ONCE
    globalNode = CreateObject("roSGNode", "GlobalNode")
    
    ' Initialize GrowthBook once
    gb = GrowthBook({
        clientKey: "sdk_YOUR_KEY",
        attributes: { id: "user123" }
    })
    if gb.init()
        ' Store globally to reuse everywhere
        globalNode.addFields({ growthBook: gb })
    end if
    
    ' Now use it throughout your app
    ' Access via: globalNode.growthBook
end function

Updating Attributes vs. Recreating Instance

' ✅ DO THIS: Update attributes without recreating instance
m.global.gb.setAttributes({
    id: newUserId,
    subscription: "premium",
    country: "US"
})

' ❌ DON'T DO THIS: Creating new instance to change attributes
m.global.gb = GrowthBook({ ... })  ' Wrong!
m.global.gb.init()                 ' Wrong!

Detailed Examples

Example 1: Basic Configuration & Instantiation

'
' Complete example showing SDK configuration and initialization
'

function InitializeGrowthBook() as object
    ' Step 1: Create configuration object
    config = {
        apiHost: "https://cdn.growthbook.io",
        clientKey: "sdk_YOUR_CLIENT_KEY",
        
        ' Set user attributes for targeting
        attributes: {
            id: "user-" + GetUserId(),           ' Unique user ID
            email: GetUserEmail(),                ' User email
            subscription: GetSubscriptionTier(), ' free, basic, premium, enterprise
            country: "US",                        ' User location
            isPremium: (GetSubscriptionTier() = "premium")
        },
        
        ' Enable debug logging during development
        enableDevMode: false
    }
    
    ' Step 2: Create GrowthBook instance
    gb = GrowthBook(config)
    
    ' Step 3: Initialize - load features from API
    if gb.init()
        print "✓ GrowthBook initialized successfully"
        print "  Features loaded: " + Str(gb.features.Count())
    else
        print "✗ Failed to initialize GrowthBook"
        ' Optionally continue with defaults or cached features
        return invalid
    end if
    
    return gb
end function

function GetUserId() as string
    ' Get from your app's user data
    return "12345"
end function

function GetUserEmail() as string
    return "[email protected]"
end function

function GetSubscriptionTier() as string
    ' Return: "free", "basic", "premium", or "enterprise"
    return "premium"
end function

' Usage in your main application
sub Main()
    gb = InitializeGrowthBook()
    
    if gb <> invalid
        ' Now ready to use feature flags
        if gb.isOn("app-feature")
            print "Feature is enabled!"
        end if
    end if
end sub

Example 2: Setting Attributes & Evaluating Features

'
' Comprehensive example showing attribute management and feature evaluation
'

sub ManageUserAttributesAndFeatures()
    ' Initialize with basic attributes
    gb = GrowthBook({
        clientKey: "sdk_YOUR_KEY",
        attributes: {
            id: "user-123",
            country: "US"
        }
    })
    gb.init()
    
    ' =========================================================
    ' SECTION 1: Initial Feature Evaluation
    ' =========================================================
    
    print "--- Initial State ---"
    if gb.isOn("enable-new-ui")
        print "✓ New UI is enabled"
        LoadNewUserInterface()
    else
        print "✗ New UI is disabled, using legacy"
        LoadLegacyUserInterface()
    end if
    
    
    ' =========================================================
    ' SECTION 2: User Logs In - Update Attributes
    ' =========================================================
    
    ' When user logs in, update their attributes
    gb.setAttributes({
        id: "user-456",                    ' Now we know their real ID
        email: "[email protected]",         ' Email address
        subscription: "premium",           ' Premium subscriber
        country: "US",
        accountAgeInDays: 365,             ' Account created a year ago
        watchTimeMinutes: 50000            ' User has watched a lot
    })
    
    print ""
    print "--- After User Login ---"
    
    
    ' =========================================================
    ' SECTION 3: Evaluate Features with New Attributes
    ' =========================================================
    
    ' Feature 1: Premium feature (only for premium users)
    if gb.isOn("premium-features")
        print "✓ Premium features available"
        ShowPremiumContent()
    else
        print "✗ Premium features not available"
    end if
    
    ' Feature 2: Get specific value with fallback
    maxQuality = gb.getFeatureValue("max-video-quality", "1080p")
    print "  Max quality: " + maxQuality
    
    ' Feature 3: Complex object configuration
    playerSettings = gb.getFeatureValue("player-config", {
        autoplay: false,
        quality: "HD",
        subtitles: "on",
        dolbyVision: false
    })
    
    print "  Player autoplay: " + Str(playerSettings.autoplay)
    print "  Player quality: " + playerSettings.quality
    
    
    ' =========================================================
    ' SECTION 4: Detailed Feature Evaluation
    ' =========================================================
    
    ' Get detailed evaluation including source and targeting info
    result = gb.evalFeature("advanced-search")
    
    print ""
    print "--- Feature Evaluation Details ---"
    print "Feature: " + result.key
    print "Enabled: " + Str(result.on)
    print "Value: " + Str(result.value)
    print "Source: " + result.source  ' defaultValue, force, experiment, or unknownFeature
    
    if result.source = "experiment"
        print "Experiment: " + result.experimentId
        print "Variation: " + Str(result.variationId)
    end if
    
    
    ' =========================================================
    ' SECTION 5: Attribute Updates Over Time
    ' =========================================================
    
    ' User upgrades subscription
    print ""
    print "--- User Upgrades Subscription ---"
    gb.setAttributes({
        id: "user-456",
        email: "[email protected]",
        subscription: "enterprise",        ' Now enterprise user!
        country: "US",
        accountAgeInDays: 365,
        watchTimeMinutes: 50000
    })
    
    ' Re-evaluate premium features
    if gb.isOn("premium-features")
        print "✓ Enterprise features now available"
    end if
    
    ' Get enterprise-specific feature
    apiLimit = gb.getFeatureValue("api-rate-limit", 1000)
    print "  API rate limit: " + Str(apiLimit) + " requests/day"
    
end sub

function LoadNewUserInterface()
    print "Loading new UI..."
end function

function LoadLegacyUserInterface()
    print "Loading legacy UI..."
end function

function ShowPremiumContent()
    print "Showing premium content..."
end function

Example 3: Advanced Targeting & Experiment Tracking

'
' Advanced example with complex targeting conditions and experiment tracking
'

sub AdvancedTargetingAndExperiments()
    ' Initialize with tracking callback
    config = {
        clientKey: "sdk_YOUR_KEY",
        attributes: {
            id: "user-789",
            subscription: "premium",
            country: "US",
            accountAge: 200,
            isTestUser: false
        },
        
        ' Callback fired when user enters an experiment (assigned to a variation)
        ' Use this to send data to your analytics platform (Firebase, Mixpanel, etc.)
        trackingCallback: sub(experiment, result)
            ' experiment: { key: string, variations: [] }
            ' result: { variationId: integer, value: dynamic, source: string, ruleId: string, experimentId: string }
            
            print "[EXPERIMENT TRACKED]"
            print "  Experiment ID: " + experiment.key
            print "  Variation ID: " + Str(result.variationId)
            
            ' Send to your analytics platform
            SendAnalyticsEvent({
                event: "experiment_exposed",
                experimentId: result.experimentId,
                variationId: result.variationId,
                ruleId: result.ruleId,
                value: result.value
            })
        end sub,

        ' Optional: Fired on every feature evaluation (not just experiments)
        onFeatureUsage: sub(featureKey, result)
            ' Use this for high-level usage tracking or debugging
            ' result: { value: dynamic, on: boolean, off: boolean, source: string, ... }
        end sub
    }
    
    gb = GrowthBook(config)
    gb.init()
    
    
    ' =========================================================
    ' TARGETING BY SUBSCRIPTION LEVEL
    ' =========================================================
    
    print "--- Premium Features (Subscription Targeting) ---"
    
    ' This feature only shows for premium/enterprise users
    if gb.isOn("advanced-analytics")
        print "✓ Advanced analytics available for premium users"
        ShowAdvancedAnalytics()
    end if
    
    
    ' =========================================================
    ' TARGETING BY COUNTRY
    ' =========================================================
    
    print ""
    print "--- Regional Features (Country Targeting) ---"
    
    ' Feature targeted to US & Canada only
    if gb.isOn("hdr-streaming")
        print "✓ HDR streaming enabled for North America"
    else
        print "✗ HDR streaming not available in your region"
    end if
    
    
    ' =========================================================
    ' A/B TESTING - BUTTON COLOR EXPERIMENT
    ' =========================================================
    
    print ""
    print "--- A/B Testing: Button Color ---"
    
    ' Get button color from experiment
    ' User will be consistently assigned to same variation
    buttonColorResult = gb.evalFeature("button-color-test")
    
    ' buttonColorResult will contain:
    ' - value: the assigned color
    ' - variationId: 0 or 1 (which variation they're in)
    ' - source: "experiment" if in test, "defaultValue" if not
    ' - experimentId: ID of the experiment
    
    buttonColor = buttonColorResult.value
    if buttonColorResult.source = "experiment"
        print "User in A/B test (Variation " + Str(buttonColorResult.variationId) + ")"
        print "Button color: " + Str(buttonColor)
    else
        print "Using default button color: " + Str(buttonColor)
    end if
    
    ' Store for later conversion tracking
    m.global.addFields({
        currentButtonColor: buttonColor,
        buttonExperimentId: buttonColorResult.experimentId
    })
    
    
    ' =========================================================
    ' A/B TESTING - PROGRESSIVE ROLLOUT
    ' =========================================================
    
    print ""
    print "--- Progressive Rollout: New Video Player ---"
    
    ' This feature gradually rolls out to percentage of users
    ' based on consistent hash of user ID
    useNewPlayer = gb.isOn("new-video-player-rollout")
    
    if useNewPlayer
        print "✓ Using new video player (progressive rollout)"
        m.player = CreateObject("roSGNode", "NewVideoPlayer")
    else
        print "✗ Using legacy video player"
        m.player = CreateObject("roSGNode", "LegacyVideoPlayer")
    end if
    
    
    ' =========================================================
    ' COMPLEX TARGETING - MULTI-CONDITION
    ' =========================================================
    
    print ""
    print "--- Complex Targeting: Premium US Users Only ---"
    
    ' This feature requires multiple conditions:
    ' - subscription = "premium" OR "enterprise"
    ' - country = "US"
    ' - accountAge > 30 days
    
    if gb.isOn("vip-support-exclusive")
        print "✓ VIP support available"
        ShowVIPSupport()
    else
        print "✗ VIP support not available for this user"
    end if
    
    
    ' =========================================================
    ' FEATURE VALUE WITH COMPLEX OBJECT
    ' =========================================================
    
    print ""
    print "--- Complex Feature Configuration ---"
    
    playerConfig = gb.getFeatureValue("player-configuration", {
        autoplay: false,
        quality: "1080p",
        bitrateLimit: 10000,
        enableSubtitles: true,
        enableDolbyVision: false,
        cacheSize: 500
    })
    
    print "Player settings:"
    print "  Autoplay: " + Str(playerConfig.autoplay)
    print "  Quality: " + playerConfig.quality
    print "  Bitrate limit: " + Str(playerConfig.bitrateLimit) + " Kbps"
    print "  Subtitles: " + Str(playerConfig.enableSubtitles)
    print "  Dolby Vision: " + Str(playerConfig.enableDolbyVision)
    print "  Cache size: " + Str(playerConfig.cacheSize) + " MB"
    
    ' Apply to player
    m.player.autoplay = playerConfig.autoplay
    m.player.quality = playerConfig.quality
    m.player.bitrateLimit = playerConfig.bitrateLimit
    
    
    ' =========================================================
    ' CONVERSION TRACKING (Manual)
    ' =========================================================
    
    ' When user completes an action (like purchase):
    ' You track it manually with the experiment context
    
end sub

function ShowAdvancedAnalytics()
    print "Showing advanced analytics dashboard..."
end function

function ShowVIPSupport()
    print "Showing VIP support options..."
end function

sub SendAnalyticsEvent(event as object)
    ' Send event to your analytics service
    ' Example: send to Roku analytics, Firebase, Mixpanel, etc.
    print "[ANALYTICS] Event: " + event.event
end sub

Installation

Option 1: Manual Installation (Recommended)

  1. Download GrowthBook.brs
  2. Copy to your channel's source/ directory
  3. Initialize in your main scene or component

Option 2: Git Submodule

cd your-roku-channel
git submodule add https://github.com/growthbook/growthbook-roku.git lib/growthbook

Then reference the SDK:

' In your main.brs
' Roku will automatically include all .brs files from source/

Documentation

Configuration Options

| Option | Type | Description | |--------|------|-------------| | apiHost | string | GrowthBook API host (default: https://cdn.growthbook.io) | | clientKey | string | Required - Your SDK client key from GrowthBook | | decryptionKey | string | Decrypt encrypted feature payloads (Roku OS 9.2+) | | attributes | object | User attributes for targeting (e.g., {id: "user-123", premium: true}) | | trackingCallback | function | Callback fired when user is placed in an experiment | | onFeatureUsage | function | Callback fired on every feature evaluation | | stickyBucketService | object | Sticky bucket storage service (in-memory or registry-based) | | enableDevMode | boolean | Enable verbose logging for debugging |

Core Methods

init() as boolean

Load features from GrowthBook API. Returns true on success.

if gb.init()
    print "Features loaded successfully"
end if

isOn(featureKey as string) as boolean

Check if a boolean feature flag is enabled.

if gb.isOn("dark-mode") then
    applyDarkTheme()
end if

getFeatureValue(featureKey as string, fallback as dynamic) as dynamic

Get the value of a feature flag with a fallback.

theme = gb.getFeatureValue("theme-color", "#0000FF")
maxItems = gb.getFeatureValue("max-items", 20)

setAttributes(attributes as object)

Update user attributes for targeting.

gb.setAttributes({
    id: userId,
    subscription: "premium",
    country: "US"
})

refreshFeatures() as boolean

Re-fetch features from the GrowthBook API. Useful for long-running apps.

if gb.refreshFeatures()
    print "Features refreshed"
end if

registerTrackingPlugin(plugin as object)

Register a tracking plugin to receive experiment and feature usage events.

plugin = GrowthBookTrackingPlugin({
    ingestorHost: "https://analytics.example.com",
    clientKey: "sdk_YOUR_KEY"
})
gb.registerTrackingPlugin(plugin)

evalFeature(featureKey as string) as object

Get detailed feature evaluation result.

result = gb.evalFeature("pricing-test")
print result.value       ' The feature value
print result.source      ' Where it came from: "defaultValue", "force", "experiment"
print result.on          ' Boolean: is feature "on"
print result.ruleId      ' ID of the matching rule

Advanced Targeting

The SDK supports all GrowthBook advanced targeting rules:

  • Filters: Complex user segmentation using hash-based filtering.
  • Ranges (Intervals): Precise traffic control for experiments and rollouts.
  • Namespaces: Mutually exclusive experiments to prevent overlap.
  • Prerequisites: Dependent feature flags (flag A requires flag B).
  • Forced Variations: Global variation overrides for testing.
  • Sticky Bucketing: Persistent variation assignments across sessions.

These are handled automatically by the SDK based on your GrowthBook configuration.

Encrypted Features

Decrypt encrypted feature payloads from the GrowthBook API. Requires Roku OS 9.2+.

gb = GrowthBook({
    clientKey: "sdk_YOUR_KEY",
    decryptionKey: "your-base64-decryption-key",
    attributes: { id: "user-123" }
})
gb.init()  ' Encrypted features are decrypted automatically

Sticky Bucketing

Persist experiment assignments so users always see the same variation, even across sessions or after attribute changes.

' In-memory (for testing or single-session use)
sbs = GrowthBookInMemoryStickyBucketService()

' Registry-based (persists across app restarts)
sbs = GrowthBookRegistryStickyBucketService()

gb = GrowthBook({
    clientKey: "sdk_YOUR_KEY",
    attributes: { id: "user-123" },
    stickyBucketService: sbs
})
gb.init()

Tracking Plugins

Register plugins to receive experiment and feature usage events. The built-in GrowthBookTrackingPlugin sends batched events to a configurable HTTP endpoint.

plugin = GrowthBookTrackingPlugin({
    ingestorHost: "https://analytics.example.com",
    clientKey: "sdk_YOUR_KEY",
    batchSize: 10
})

gb = GrowthBook({
    clientKey: "sdk_YOUR_KEY",
    attributes: { id: "user-123" }
})
gb.init()
gb.registerTrackingPlugin(plugin)

' Events are batched and sent automatically
if gb.isOn("new-feature") then doSomething()

Examples

See the examples/ directory for complete working examples:

  • simple_flag.brs - Basic feature flag usage
  • experiments.brs - A/B testing with tracking
  • targeting.brs - Advanced audience targeting
  • coverage_rollout.brs - Progressive feature rollouts
  • array_targeting.brs - Tag-based targeting with arrays
  • version_targeting.brs - Semantic version targeting
  • weighted_experiments.brs - Custom traffic splits
  • consistent_hashing.brs - User bucketing consistency

Testing

Run All Tests

npm test

This runs both the JS validator (327 spec tests) and native BrightScript runner (401 tests) in sequence.

Individual Test Commands

| Command | What It Does | |---------|--------------| | npm run test:validator | JS logic validator against cases.json (fast, no device needed) | | npm run test:native | Native BrightScript tests via brs-engine (runs actual .brs code) | | npm run test:channel | Build test channel for brs-desktop on-device validation | | npm run lint | BrightScript syntax and lint checks |

On-Device Testing (brs-desktop)

For testing in a full Roku-like runtime with SceneGraph:

npm run test:channel

Then open test-channel/pkg.zip in brs-desktop. See test-channel/README.md for details.

See docs/TESTING.md for the comprehensive testing guide.

Performance

Benchmarks on Roku Ultra (2023):

| Operation | Time | Notes | |-----------|------|-------| | SDK Initialization | ~50ms | Without network call | | Feature Load (API) | 500-2000ms | First load from network | | Feature Evaluation | <1ms | Cached, in-memory | | Experiment Evaluation | <2ms | With hashing & targeting |

Memory Usage: ~150KB total (SDK + cached features)

Browser Support

Works on all Roku devices:

  • ✅ Roku Ultra
  • ✅ Roku Streaming Stick
  • ✅ Roku Express
  • ✅ Roku TV
  • ✅ Roku Premiere
  • ✅ Legacy Roku devices (Roku 2/3)

Minimum Roku OS: 9.0+

Limitations

  • No Server-Sent Events (SSE) streaming support (Roku limitation)
  • No Visual Editor experiments (SceneGraph only)
  • Encrypted features require roEVPCipher component (Roku OS 9.2+)
  • Network requests are synchronous (no background polling)

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Development Setup

  1. Fork and clone the repo
  2. Make changes to source/GrowthBook.brs
  3. Test with npm test
  4. Deploy to Roku device for integration testing
  5. Submit a pull request

Running Tests

npm install
npm test

FAQ

Q: Does this work with SceneGraph?
A: Yes! Initialize GrowthBook in your Scene component and access it throughout your SceneGraph tree.

Q: Can I use this offline?
A: Yes, features are cached after the first load. Pass features directly to skip network calls:

gb = GrowthBook({features: myFeaturesObject})

Q: How do I target by device type?
A: Set device info as attributes:

deviceInfo = CreateObject("roDeviceInfo")
gb.setAttributes({
    id: userId,
    deviceType: deviceInfo.GetModel(),
    osVersion: deviceInfo.GetVersion()
})

Q: Does this support remote evaluation?
A: No, Roku SDK uses local evaluation only. Features are evaluated on the device.

Related SDKs

Resources

License

MIT License - see LICENSE file for details.

Support


Made with ❤️ by the GrowthBook team