Skip to content

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

  1. Click "New API Token"
  2. Add a token name
  3. Select "Server-side SDK (CLIENT)"
  4. Select the environment (dev, stage, prod)
  5. 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:

  1. Call the feature-toggle in the loader
  2. Forward the value to the component
  3. 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.ts
  • app/feature-toggles/newsletter-title-change-feature-toggle.server.ts

Each file should export:

  • name - The feature toggle identifier
  • experimentId - 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 statistaContentView cookie 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.

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.