Gatsby SDK
Toggly's Gatsby SDK provides comprehensive feature flag management for Gatsby applications with support for SSR, SSG, build-time page gating, and React-based components.
Grab the printable Gatsby cheat sheet (download PDF) — plugin config, React hooks, build-time page gating.
Overview
The Toggly Gatsby SDK offers:
- Gatsby Plugin Architecture - Seamless integration with Gatsby's plugin system
- React Hooks - Modern
useFeatureFlag,useFeatureGate, anduseTogglyhooks - React Components - Declarative
FeatureandFeatureGatecomponents - Page-Level Gating - Control page visibility via frontmatter
- SSR & SSG Support - Works with both rendering modes
- Build-Time Override - Optionally enable all features during build for edge filtering
- User Targeting - Identity-based feature rollouts
- Reactive State - Powered by nanostores for lightweight, framework-agnostic state
- Edge Ready - Optional Cloudflare Worker integration for true enforcement
Requirements
- Gatsby: 5.0.0 or higher
- React: 18.x
- Node.js: 18.0.0 or higher
Installation
Install the Gatsby SDK using NPM:
npm install @ops-ai/gatsby-feature-flags-toggly
The SDK includes peer dependencies for nanostores and React integration:
npm install nanostores @nanostores/react react react-dom
Configuration
1. Add the Plugin
Configure the Toggly plugin in your gatsby-config.js:
module.exports = {
siteMetadata: {
title: 'My Gatsby Site',
},
plugins: [
{
resolve: '@ops-ai/gatsby-feature-flags-toggly',
options: {
// Replace with your actual app key from toggly.io
appKey: process.env.TOGGLY_APP_KEY || 'your-app-key',
environment: process.env.TOGGLY_ENVIRONMENT || 'Production',
// Enable all features during build for demonstration
allFeaturesEnabledDuringBuild: true,
// Fallback values when API is unavailable
flagDefaults: {
'new-dashboard': false,
'beta-feature': false,
'premium-content': false,
},
// Init-time targeting (runtime setIdentity updates identity only)
// identity: 'user-123',
// groups: ['beta'],
// claims: { plan: 'premium' },
// Live updates + signing
enableLiveUpdates: true, // default — browser WebSocket sync
// verifySignatures: true,
// Refresh interval in milliseconds (default: 3 minutes)
featureFlagsRefreshInterval: 180000,
// Enable debug logging
isDebug: process.env.NODE_ENV === 'development',
// API timeout in milliseconds
connectTimeout: 5000,
// Optional error reporting callback
onError: (message, error) => {
console.warn('Toggly error:', message, error);
},
},
},
],
};
Browser crypto bundling
If the browser build reports that it cannot resolve crypto from the signature verifier, add the following to gatsby-node.js. Merge this logic into an existing onCreateWebpackConfig export if your application already has one.
exports.onCreateWebpackConfig = ({ stage, actions }) => {
if (stage === 'build-javascript' || stage === 'develop') {
actions.setWebpackConfig({
resolve: { fallback: { crypto: false } },
});
}
};
The SDK uses native WebCrypto in the browser; the Node crypto branch is not needed there. This browser-only fallback leaves Gatsby's Node HTML-rendering stages unchanged and does not disable signature verification. Use HTTPS or localhost so WebCrypto is available, and keep verifySignatures: true when configuring signed evaluation.
This setting resolves bundling only. It does not connect the configured plugin's client state to separately imported package-root hooks or components. If those hooks remain in a loading state after the plugin fetches definitions, check their client initialization separately; adding a webpack fallback does not resolve that state mismatch.
See Gatsby's custom webpack configuration and webpack's resolve.fallback. Run gatsby build after changing the configuration.
2. Environment Variables
Create a .env file with your Toggly credentials:
TOGGLY_APP_KEY=your-app-key
TOGGLY_ENVIRONMENT=Production
Get your App Key from the Toggly application settings. Make sure to enable the Client Side API and create a Frontend-type key.
3. Build-Time Strategy
The SDK supports two approaches for handling feature flags at build time:
Approach 1: Build with All Features Enabled (Recommended)
Enable all features during build and filter at the edge or runtime:
{
resolve: '@ops-ai/gatsby-feature-flags-toggly',
options: {
appKey: process.env.TOGGLY_APP_KEY,
environment: 'Production',
allFeaturesEnabledDuringBuild: true, // Build everything
},
}
Pros:
- Single build for all flag states
- Fast deployments (no rebuild needed)
- Edge worker can filter content dynamically
- Client-side components can update without rebuild
Use when: You have edge filtering or want maximum deployment flexibility
Approach 2: Build with Actual Flag Values
Fetch actual flag values during build:
{
resolve: '@ops-ai/gatsby-feature-flags-toggly',
options: {
appKey: process.env.TOGGLY_APP_KEY,
environment: 'Production',
allFeaturesEnabledDuringBuild: false, // Fetch real values
},
}
Pros:
- Smaller bundle size (only enabled features built)
- True static site optimization
- No edge worker needed
Cons:
- Requires rebuild to change flags
- Separate builds for different flag states
Use when: You have a true static site with infrequent flag changes
Error reporting and fallback behavior
Pass onError in gatsby-config.js to observe fetch, cache, parse, and refresh
failures:
{
resolve: '@ops-ai/gatsby-feature-flags-toggly',
options: {
appKey: process.env.TOGGLY_APP_KEY,
environment: 'Production',
onError: (message, error) => {
monitoring.captureException(error, {
tags: { source: 'toggly' },
extra: { message }
});
},
},
}
The Gatsby client store exposes error through useToggly(). After one
successful load, refresh failures preserve the last-known-good flags instead of
replacing rendered content with defaults or empty flags. Hooks and components
subscribe to flag refreshes, so client-side UI can update after interval
refreshes.
For the shared reliability contract, see Reliability and Error Handling.
React Hooks
The SDK provides modern React hooks for feature flag evaluation.
useFeatureFlag
Check if a single feature flag is enabled:
import { useFeatureFlag } from '@ops-ai/gatsby-feature-flags-toggly';
export function Dashboard() {
const { enabled, isReady } = useFeatureFlag('new-dashboard');
if (!isReady) {
return <Loading />;
}
return enabled ? <NewDashboard /> : <OldDashboard />;
}
Hook Signature:
function useFeatureFlag(
key: string,
defaultValue?: boolean
): {
enabled: boolean;
isReady: boolean;
}
useFeatureGate
Evaluate multiple feature flags with logical operators:
import { useFeatureGate } from '@ops-ai/gatsby-feature-flags-toggly';
export function PremiumContent() {
// All flags must be enabled
const { enabled: hasFullAccess } = useFeatureGate(
['premium-tier', 'content-access'],
'all'
);
// At least one flag must be enabled
const { enabled: hasAnyAccess } = useFeatureGate(
['trial-access', 'premium-tier'],
'any'
);
return hasFullAccess ? <FullContent /> : <LimitedContent />;
}
Hook Signature:
function useFeatureGate(
keys: string[],
requirement?: 'all' | 'any',
negate?: boolean
): {
enabled: boolean;
isReady: boolean;
}
useToggly
Access the full Toggly client for advanced operations:
import { useToggly } from '@ops-ai/gatsby-feature-flags-toggly';
export function FeatureManager() {
const { flags, isReady, error, refreshFlags, setIdentity } = useToggly();
const handleLogin = async (userId: string) => {
// Set user identity for targeting
setIdentity(userId);
// The setter starts the refresh; observe flags/error for its result.
};
return (
<div>
{error && <ErrorBanner error={error} />}
<button onClick={refreshFlags}>Refresh Flags</button>
<FeatureList flags={flags} />
</div>
);
}
Hook Signature:
function useToggly(): {
flags: Record<string, boolean>;
isReady: boolean;
error: Error | null;
refreshFlags: () => Promise<void>;
setIdentity: (identity: string) => void;
clearIdentity: () => void;
}
React Components
Feature Component
Conditionally render content based on a single feature flag:
Basic Usage
import { Feature } from '@ops-ai/gatsby-feature-flags-toggly';
export function HomePage() {
return (
<div>
<Feature flag="new-hero-section">
<NewHeroSection />
</Feature>
<MainContent />
</div>
);
}
On and off paths
Use a second <Feature negate> for the off path — the same pattern as Next.js and .NET <feature negate>:
<Feature flag="premium-feature">
<PremiumContent />
</Feature>
<Feature flag="premium-feature" negate>
<UpgradePrompt />
</Feature>
With Loading State
Show a loading indicator while flags are being fetched:
<Feature flag="interactive-widget" loading={<Spinner />}>
<InteractiveWidget />
</Feature>
<Feature flag="interactive-widget" negate loading={<Spinner />}>
<StaticWidget />
</Feature>
Component Props:
interface FeatureProps {
flag: string;
defaultValue?: boolean;
negate?: boolean;
loading?: React.ReactNode;
children: React.ReactNode;
}
FeatureGate Component
Evaluate multiple feature flags with logical operators:
All Flags Required
import { FeatureGate } from '@ops-ai/gatsby-feature-flags-toggly';
<FeatureGate flags={['premium-tier', 'beta-access']}>
<ExclusiveContent />
</FeatureGate>
Any Flag Required
<FeatureGate
flags={['trial-access', 'premium-tier']}
requirement="any"
>
<ProtectedContent />
</FeatureGate>
<FeatureGate
flags={['trial-access', 'premium-tier']}
requirement="any"
negate
>
<SignUpPrompt />
</FeatureGate>
With Negation
<FeatureGate
flags={['legacy-mode', 'maintenance']}
requirement="any"
negate
>
{/* Show only when NEITHER flag is enabled */}
<NormalOperation />
</FeatureGate>
Component Props:
interface FeatureGateProps {
flags: string[];
requirement?: 'all' | 'any';
negate?: boolean;
loading?: React.ReactNode;
children: React.ReactNode;
}
Page-Level Gating
Control entire pages using frontmatter. This is more efficient than wrapping all page content in a component.
MDX Pages
---
title: Beta Feature Page
x-feature: beta-feature
---
# Beta Feature
This entire page is gated by the 'beta-feature' flag.
Only users with the feature enabled will see this content.
React/JSX Pages
For .jsx or .tsx pages, you can use page context:
// src/pages/beta-feature.tsx
import React from 'react';
export const Head = () => <title>Beta Feature</title>;
// Add x-feature to page context in gatsby-node.js
const BetaFeaturePage = () => {
return (
<div>
<h1>Beta Feature Page</h1>
<p>This page is gated by the 'beta-feature' flag</p>
</div>
);
};
export default BetaFeaturePage;
The plugin automatically:
- Extracts
x-featurefrom page frontmatter during build - Generates a
toggly-page-features.jsonmanifest in thepublicfolder - Maps routes to feature flags
Edge Enforcement
For true edge enforcement (404 responses for disabled pages), deploy the Toggly Cloudflare Worker in front of your origin. It:
- Reads
/toggly-page-features.json(the plugin writes this topublic/at build time) - Fetches flags from
GET https://definitions.toggly.io/evaluated-signed/{appKey}/{environment} - Returns 404 (or a redirect) for pages whose mapped flag is off
- Strips HTML elements with
data-feature="flag_key"when that flag is off
User Identity and Targeting
The Toggly API evaluates feature flags in the context of a specific user, enabling powerful targeting and rollout capabilities.
Setting Identity
On User Login
import { useToggly } from '@ops-ai/gatsby-feature-flags-toggly';
export function LoginPage() {
const { setIdentity } = useToggly();
const handleLogin = async (user) => {
// Authenticate user...
// Set user identity for Toggly
setIdentity(user.email); // or user.id
// setIdentity starts the refresh automatically.
};
return <LoginForm onLogin={handleLogin} />;
}
On User Logout
export function LogoutButton() {
const { clearIdentity } = useToggly();
const handleLogout = async () => {
// Clear authentication...
// Clear Toggly identity
clearIdentity();
// clearIdentity starts the anonymous refresh automatically.
};
return <button onClick={handleLogout}>Logout</button>;
}
How It Works
When you provide a user identity:
- API Call: The SDK fetches flags for that user from the Toggly definitions API
- Server Evaluation: Toggly evaluates targeting rules, segments, and rollouts for this user
- Personalized Flags: Returns feature flags specific to this user
- Client Caching: Results are cached (default 3 minutes) to minimize API calls
Each user receives personalized feature flags based on:
- Targeting Rules: User attributes and conditions
- Segments: User group membership
- Percentage Rollouts: Consistent hash-based distribution
- Override Rules: Manual user/team overrides
Anonymous Users
If no identity is set, the SDK fetches flags for anonymous users. Anonymous users still receive flags, but targeting rules and percentage rollouts won't apply consistently.
Groups, claims, live updates, and signing
Pass groups and claims in plugin options at init. Runtime setIdentity
updates identity only — there is no setContext({ groups, claims }).
{
resolve: '@ops-ai/gatsby-feature-flags-toggly',
options: {
appKey: process.env.TOGGLY_APP_KEY,
environment: 'Production',
identity: 'user-123',
groups: ['beta', 'enterprise'],
claims: { role: 'admin', plan: 'premium' },
enableLiveUpdates: true, // default
verifySignatures: true,
},
}
Init groups / claims feed local EvalContext (including claims for
UserClaims). The Gatsby server rail does not populate
EvalContext.request per HTTP request, so Browser / Country / Device / OS
filters fail closed there unless you evaluate on another local-eval SDK. See
SDK × filter matrix.
See Feature filters, Evaluated-signed, and WebSocket sync.
SSR vs SSG Considerations
Server-Side Rendering (SSR)
With gatsby build and a server adapter (e.g., Gatsby Cloud, Netlify, Vercel):
- Flags are fetched at build time and embedded in the static HTML
- Client-side components can refresh flags after hydration
- User-specific flags require client-side evaluation
Static Site Generation (SSG)
With gatsby build for static hosting:
- Flags are fetched at build time
- Same flag values for all visitors until next build
- Fastest possible page loads
- Requires rebuild to update server-rendered flags
- Client-side components can still update dynamically
Hybrid Strategy
Combine build-time and runtime evaluation:
export function HybridPage() {
return (
<div>
{/* Critical features: built into static HTML */}
<Feature flag="access-control">
<SecureContent />
</Feature>
{/* Non-critical: evaluated on client after hydration */}
<Feature flag="ui-enhancement">
<EnhancedUI />
</Feature>
</div>
);
}
Advanced Features
Manual Flag Refresh
Manually refresh flags from the API:
import { useToggly } from '@ops-ai/gatsby-feature-flags-toggly';
export function RefreshButton() {
const { refreshFlags, isReady } = useToggly();
return (
<button onClick={refreshFlags} disabled={!isReady}>
Refresh Flags
</button>
);
}
Access All Flags
Get all feature flags programmatically:
import { useToggly } from '@ops-ai/gatsby-feature-flags-toggly';
export function FeatureList() {
const { flags, isReady } = useToggly();
if (!isReady) return <Loading />;
return (
<ul>
{Object.entries(flags).map(([key, enabled]) => (
<li key={key}>
{key}: {enabled ? '✅' : '❌'}
</li>
))}
</ul>
);
}
Error Handling
Handle errors gracefully:
import { useToggly } from '@ops-ai/gatsby-feature-flags-toggly';
export function FeatureWithErrorHandling() {
const { flags, error, isReady } = useToggly();
if (error) {
console.error('Failed to load flags:', error);
// Fall back to defaults
return <DefaultExperience />;
}
if (!isReady) return <Loading />;
return flags['new-feature'] ? <NewFeature /> : <OldFeature />;
}
Configuration Options
interface TogglyPluginOptions {
// Your application key from Toggly (required)
appKey: string;
// Environment name (default: 'Production')
environment?: string;
// Base URI for the Toggly API (default: 'https://definitions.toggly.io')
baseURI?: string;
// Default values when API is unavailable
flagDefaults?: Record<string, boolean>;
// Refresh interval in milliseconds (default: 180000 = 3 minutes)
featureFlagsRefreshInterval?: number;
// Browser WebSocket live updates (default: true)
enableLiveUpdates?: boolean;
// Verify ES256 signed envelopes via JWKS (default: false)
verifySignatures?: boolean;
allowedKeyIds?: string[];
maxSignatureAgeSeconds?: number;
// Enable all features during build (default: false)
// When true, all flags return true at build time
// Use with edge workers for runtime filtering
allFeaturesEnabledDuringBuild?: boolean;
// User identity for targeting (optional)
identity?: string;
// Init-time groups and claims for targeting
groups?: string[];
claims?: Record<string, string>;
// Enable debug logging (default: false)
isDebug?: boolean;
// API timeout in milliseconds (default: 5000)
connectTimeout?: number;
}
TypeScript Support
The SDK includes full TypeScript support with type definitions:
import type {
TogglyPluginOptions,
Flags,
FeatureProps,
FeatureGateProps,
UseFeatureFlagResult,
UseFeatureGateResult,
UseTogglyResult,
} from '@ops-ai/gatsby-feature-flags-toggly';
// All hooks and components are fully typed
const { enabled, isReady }: UseFeatureFlagResult = useFeatureFlag('my-flag');
Entity context
Pass a page entity per component or hook — not via global user setContext(). See Entity & page context.
This browser example passes a canonical entity directly to the native component. The separate server API also exposes TogglyServer.registerContext; do not register dashboard schemas from browser code.
// Canonical context travels with this one entity evaluation.
const orderContext = {
kind: 'Order',
key: String(order.id),
attributes: { Status: order.status, Total: order.total },
}
import { Feature } from '@ops-ai/gatsby-feature-flags-toggly'
<Feature flag="OrderBadge" context={orderContext}>
<span className="badge">Featured</span>
</Feature>
List rows share one flag fetch; each row passes its own entity. Gates fail closed without context. Identity remains user only.
Best Practices
- Set Flag Defaults - Always provide fallback values for offline/error scenarios
- Use Environment Variables - Store credentials securely, never hardcode
- Enable Debug in Development - Use
isDebug: trueto troubleshoot issues - Choose Build Strategy Wisely - Consider
allFeaturesEnabledDuringBuildvs. actual values - Provide User Identity - Required for targeting and consistent rollouts
- Handle Loading States - Always show loading indicators while
isReadyis false - Handle Errors Gracefully - Provide fallback experiences when flags fail to load
- Use Page-Level Gating - More efficient than wrapping entire page content
- Cache Appropriately - Adjust
featureFlagsRefreshIntervalbased on your needs
Complete Example
Here's a complete example combining multiple patterns:
// src/pages/index.tsx
import React from 'react';
import {
Feature,
FeatureGate,
useFeatureFlag,
useToggly
} from '@ops-ai/gatsby-feature-flags-toggly';
export const Head = () => <title>Home Page</title>;
const HomePage = () => {
const { enabled: showBanner } = useFeatureFlag('promotional-banner');
const { isReady, error } = useToggly();
if (!isReady) {
return <LoadingPage />;
}
if (error) {
console.error('Flag error:', error);
return <ErrorPage />;
}
return (
<div>
{/* Conditional rendering with hook */}
{showBanner && (
<div className="banner">
<p>Special offer - 50% off!</p>
</div>
)}
{/* Feature on / off with negate */}
<Feature flag="new-homepage-design">
<NewHomepage />
</Feature>
<Feature flag="new-homepage-design" negate>
<OldHomepage />
</Feature>
{/* Multiple flags with FeatureGate */}
<FeatureGate
flags={['premium-tier', 'beta-access']}
requirement="all"
>
<ExclusiveContent />
</FeatureGate>
<FeatureGate
flags={['premium-tier', 'beta-access']}
requirement="all"
negate
>
<UpgradePrompt />
</FeatureGate>
{/* Show when maintenance is off */}
<Feature flag="maintenance-mode" negate>
<NormalOperations />
</Feature>
</div>
);
};
export default HomePage;
Troubleshooting
Flags Not Loading
- Verify plugin is configured in
gatsby-config.js - Check
appKeyandenvironmentvalues - Enable
isDebug: trueto see logs in browser console - Check network tab for API calls to
definitions.toggly.io - Verify Client Side API is enabled in Toggly settings
- Check for CORS errors in browser console
Build Errors
- Ensure all peer dependencies are installed:
npm install nanostores @nanostores/react react react-dom
- Clear Gatsby cache and rebuild:
gatsby clean
gatsby build
- Check for plugin errors in build logs
TypeScript Errors
- Ensure TypeScript is configured:
npm install -D typescript @types/react @types/react-dom
- Create or update
tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"jsx": "react",
"strict": true,
"esModuleInterop": true
}
}
Hooks Not Updating
- Verify hooks and components read the client initialized by their provider; the plugin's separately bundled client may not initialize package-root imports
- Check that
featureFlagsRefreshIntervalis set appropriately - Use
refreshFlags()fromuseToggly()to manually refresh - Check browser console for errors
Page Gating Not Working
- Verify
x-featurefrontmatter is set correctly in MDX files - Check
public/toggly-page-features.jsonwas generated - For edge enforcement, deploy the Toggly Cloudflare Worker
- Check Cloudflare Worker logs for errors
Identity Not Working
- Call
setIdentity()after user login - Observe the refreshed flags or error;
setIdentity()already starts the request - Verify identity is sent in API requests (check network tab)
- Check Toggly dashboard for targeting rules
Performance Considerations
Bundle Size
The SDK is lightweight and tree-shakeable:
- Core SDK: ~15 KB (minified)
- Nanostores: ~1 KB (minified)
- Total impact: ~16 KB (gzipped: ~6 KB)
API Calls
- Build time: 1 API call (or 0 if
allFeaturesEnabledDuringBuild: true) - Runtime: 1 API call on initial page load, then cached
- Refresh: Configurable via
featureFlagsRefreshInterval(default: 3 minutes) - User identity change:
setIdentity()starts the user-specific refresh automatically
Caching
The SDK uses an in-memory cache with these characteristics:
- Cache duration: Configurable via
featureFlagsRefreshInterval - Cache key: Includes
appKey,environment, andidentity - Cache invalidation: Automatic after refresh interval or manual via
refreshFlags()
Extensibility with Hooks
Toggly provides a powerful hooks system that allows you to extend SDK functionality by hooking into feature flag lifecycle events. This is perfect for integrating with analytics platforms like Microsoft Clarity, monitoring tools, or implementing custom behaviors.
What are Hooks?
Hooks let you execute custom code at specific points in the feature flag evaluation lifecycle:
- beforeEvaluation: Called before a feature flag is evaluated
- afterEvaluation: Called after a feature flag is evaluated (with the result)
- beforeIdentify: Called before user identity is set or cleared
- afterIdentify: Called after user identity is set or cleared
- afterRefresh: Called after feature definitions are refreshed from Toggly
Creating a Hook
import { Hook } from '@ops-ai/toggly-hooks-types';
const myAnalyticsHook: Hook = {
getMetadata: () => ({
name: 'MyAnalyticsHook',
version: '1.0.0'
}),
afterEvaluation: async (flagKey, _data, result) => {
// Send to analytics
if (typeof analytics !== 'undefined') {
analytics.track('Feature Flag Evaluated', {
feature: flagKey,
enabled: result
});
}
}
};
Registering Hooks
During initialization in gatsby-config:
// gatsby-config.js
module.exports = {
plugins: [
{
resolve: '@ops-ai/gatsby-feature-flags-toggly',
options: {
appKey: 'your-app-key',
environment: 'your-environment-name',
hooks: [myAnalyticsHook]
}
}
]
};
At runtime:
import { addHook, removeHook } from '@ops-ai/gatsby-feature-flags-toggly';
// Add a hook
addHook(myAnalyticsHook);
// Remove a hook
removeHook(myAnalyticsHook.getMetadata().name);
In a Gatsby component:
import React, { useEffect } from 'react';
import { addHook, removeHook } from '@ops-ai/gatsby-feature-flags-toggly';
import type { Hook } from '@ops-ai/toggly-hooks-types';
const analyticsHook: Hook = {
getMetadata: () => ({ name: 'Analytics', version: '1.0.0' }),
afterEvaluation: async (flagKey, _data, result) => {
console.log('Feature evaluated:', flagKey, result);
}
};
export default function MyPage() {
useEffect(() => {
addHook(analyticsHook);
return () => { removeHook(analyticsHook.getMetadata().name); };
}, []);
return <div>...</div>;
}
Common Use Cases
Microsoft Clarity Integration
import type { Hook } from '@ops-ai/toggly-hooks-types';
const clarityHook: Hook = {
getMetadata: () => ({ name: 'Microsoft Clarity', version: '1.0.0' }),
afterEvaluation: async (flagKey, _data, result) => {
if (typeof clarity !== 'undefined') {
clarity('event', `FeatureFlag:${flagKey}`);
}
}
};
Google Analytics 4 Integration
import type { Hook } from '@ops-ai/toggly-hooks-types';
const ga4Hook: Hook = {
getMetadata: () => ({ name: 'Google Analytics 4', version: '1.0.0' }),
afterEvaluation: async (flagKey, _data, result) => {
if (typeof gtag !== 'undefined') {
gtag('event', 'feature_flag_evaluated', {
feature_name: flagKey,
feature_enabled: result
});
}
}
};
Debug Logging (Development Only)
import type { Hook } from '@ops-ai/toggly-hooks-types';
const debugHook: Hook = {
getMetadata: () => ({ name: 'DebugLogger', version: '1.0.0' }),
afterEvaluation: async (flagKey, _data, result) => {
if (process.env.NODE_ENV === 'development') {
console.debug('[Toggly]', flagKey, '=', result);
}
}
};
See also
Sample references
Browse the Toggly Samples catalog for hands-on examples. Start with a sample’s README for setup instructions, then follow its source walkthrough to see how configuration, flag checks, and UI behavior fit together.
Next Steps
- Learn about User Targeting and Rollouts
- Explore Metrics and Monitoring
- Check out Cloudflare Workers Integration
- Read about Other JavaScript SDKs
- Explore React SDK for Create React App, Next.js, etc.