Unleash Feature Toggles in Remix
Prerequisites
To use Unleash, you need to request access from Okta.
Login at Unleash hosted using "Sign in with SAML 2.0" and create a project for your application.
Environment Setup
Configure these environment variables for each environment (dev, stage, prod):
APP_UNLEASH_ENV- The environment name (e.g., "dev", "stage", "prod"). This tells Unleash which environment-specific toggle configurations to load.APP_UNLEASH_APP_NAME- Your application's unique identifier in Unleash. This groups your feature toggles under your specific project.APP_UNLEASH_SERVER_TOKEN- The server-side SDK authentication token. This securely connects your application to Unleash's API to fetch toggle states.
Get app-name and tokens from your project's API access settings in Unleash.
Token Creation Steps
- Click "New API Token"
- Add a token name
- Select "Server-side SDK (CLIENT)"
- Select the environment (dev, stage, prod)
- Click "Create token"
Add tokens as GitHub environment secrets and optionally to your local .env
file.
Usage Types
Feature toggles work server-side only. While you could use them directly in
loaders and actions, this is not recommended as it leads to scattered
feature toggle logic, makes testing difficult, complicates maintenance, reduces
reusability across different routes, and makes it harder to track which toggles
exist in your application. Instead, create dedicated files for each feature
toggle (e.g. newsletter-feature-toggle.server.ts). This approach allows you to
specify the toggle name, handle errors, centralize everything relevant to that
specific feature toggle, and maintain clean separation of concerns.
Kill-Switch Feature Toggles
Use kill-switches to control feature visibility and enable confident deployment.
Implementation Pattern:
- Call the feature-toggle in the loader
- Forward the value to the component
- Toggle the component/feature based on the value
import { newsletterSignupFeatureToggle } from "../feature-toggles/newsletter-feature-toggle.server.js";
export async function loader({ request }: LoaderFunctionArgs) {
const { enabled: isNewsletterSignupEnabled } =
await newsletterSignupFeatureToggle(request);
return json({
isNewsletterSignupEnabled,
});
}
export default function Index() {
const { isNewsletterSignupEnabled } = useLoaderData<typeof loader>();
return (
<div>{isNewsletterSignupEnabled ? <NewsletterSignup /> : undefined}</div>
);
}
Local Testing: To test this feature toggle locally, add a cookie named
feature-toggles with the value enable_newsletter_signup in your browser's
developer tools. The value enable_newsletter_signup must match the name
constant defined in the feature toggle file because the code uses
getCookieValue(request, name) to check if the toggle should be enabled
locally. This is already set up in the newsletter-feature-toggle file.
Multi-Variant Tests (A/B/X)
For A/B testing, use variants instead of simple enabled/disabled states. Variant
A serves as the default fallback. The variant names (A, B, C, etc.) and their
specific implementations are defined by your team based on your experiment
requirements - the example below demonstrates the pattern but you can customize
the variants and their behavior as needed. This is already set up by default in
the newsletter-title-change-feature-toggle file.
import { newsletterTitleChangeFeatureToggle } from "../feature-toggles/newsletter-title-change-feature-toggle.server.js";
export async function loader({ request }: LoaderFunctionArgs) {
const { variant: newsletterVariant } =
await newsletterTitleChangeFeatureToggle(request);
let buttonColor;
switch (newsletterVariant) {
case "B": {
buttonColor = "green";
break;
}
case "C": {
buttonColor = "red";
break;
}
default: {
buttonColor = "blue";
}
}
return json({
buttonColor,
});
}
export default function Index() {
const { buttonColor } = useLoaderData<typeof loader>();
return <NewsletterSignup buttonColor={buttonColor} />;
}
Local Testing: To test variants locally, add a cookie named
feature-toggles with the value
enable_newsletter_signup,enable_newsletter_signup_title_change=B to test
variant B, or enable_newsletter_signup_title_change=A for variant A.
Analytics Integration
For A/B tests, you need to track which variant users are seeing.
Setting Up Test Variants
import {
experimentId,
newsletterTitleChangeFeatureToggle,
name,
} from "../feature-toggles/newsletter-title-change-feature-toggle.server.js";
export type TestVariants = {
featureName: string;
userTestVariant: string;
experimentId: string;
experimentNumber?: string;
}[];
export const getTestVariants = async (
request: Request,
): Promise<TestVariants> => {
const featureToggle = await newsletterTitleChangeFeatureToggle(request);
if (!featureToggle.enabled || !featureToggle.variant) {
return [];
}
return [
{
featureName: name,
userTestVariant: featureToggle.variant.name,
experimentId,
},
];
};
This data is then passed to the data tracking team to ensure proper A/B test measurement and analysis.
Using Test Variants in Analytics Component
The Analytics component is already set up in your app's root layout and can handle multiple A/B tests simultaneously. You don't need to render it per feature toggle - instead, collect all test variants in your loader and pass them to the existing Analytics component:
import { getTestVariants } from "../analytics/test-variants.server.js";
export async function loader({ request }: LoaderFunctionArgs) {
const { enabled: isNewsletterSignupEnabled } =
await newsletterSignupFeatureToggle(request);
// This collects ALL active test variants across your app
const testVariants = await getTestVariants(request);
return json({
isNewsletterSignupEnabled,
testVariants, // Array of all active A/B tests
});
}
export default function Index() {
const { isNewsletterSignupEnabled, testVariants } =
useLoaderData<typeof loader>();
return (
<>
<Analytics testVariants={testVariants} />
{isNewsletterSignupEnabled ? <NewsletterSignup /> : undefined}
</>
);
}
Multiple A/B Tests: The getTestVariants function automatically collects
all active feature toggle variants across your application. When you have
multiple A/B tests running simultaneously, the function returns an array
containing all active test assignments, ensuring comprehensive analytics
tracking without conflicts.
The Analytics component will automatically initialize GTM with all test variant data in a useEffect, ensuring proper tracking of multiple A/B test assignments simultaneously.
File Organization
Create feature toggle files in a directory of your choosing with descriptive
names that include .server.ts suffix:
app/feature-toggles/newsletter-feature-toggle.server.tsapp/feature-toggles/newsletter-title-change-feature-toggle.server.ts
Each file should export:
name- The feature toggle identifierexperimentId- If using A/B tests (used in analytics)- The main feature toggle function
Error Handling
Feature toggles include comprehensive error handling:
- Fallback behavior: If Unleash fails, toggles default to disabled/variant A
- User targeting: Uses
statistaContentViewcookie for consistent user session assignment to variants - Logging: Errors are logged for monitoring and debugging
Mocks
Feature toggles need to be mocked to ensure reliable and stable development/test environments. That's why feature toggles are mocked for the development environment.
Unleash returns an array of defined feature toggles for the project, including their enabled state and assigned variants. When adding new feature toggles, they must be added to the mock data as well. New toggles should default to disabled state in mocks.
Cookie-based Testing
Enable toggles during development using a feature-toggles cookie with
comma-separated toggle names:
- Enable:
enable_newsletter_signup,enable_xyz - Variants:
enable_newsletter_signup_title_change=B
This allows flexible development and testing of different feature states.