Global variants

Earlier we describe variants, which are always specific to a component. For instance, the primary variant of a button.

However, if you want to re-theme the entire app, such as adding a “Dark mode,” you probably want to change many components together.

Global variants are similar to normal (component-specific) variants, in that components can appear differently in different global variants. However, global variants themselves are global to the entire project, meaning that any component can have specific style settings defined under a single “Dark mode” variant.

Besides dark mode, there are many other use cases for global variants, where you want many components in the app to change together:

  • platform—web vs. iOS vs. Android
  • screen size—mobile vs. desktop
  • palette—light mode vs. dark mode vs. sepia vs. high-contrast dark
  • locale—LTR vs. RTL languages
  • brand—Facebook vs. Instagram vs. WhatsApp, or Material vs. Cloud Material
  • configurations—Gmail roomy vs. dense

For instance, a global variant for iOS vs. Android might switch over the look of many components:

This is a particularly powerful feature for designing robust design system components that allow designers to view their designs across multiple contexts.

To create and manage global variants, select into any component to bring up the floating variants panel, and in the bottom section you’ll see the global variants listed, with the ability to create more.

You can then set any artboard to render its contents in the context of some global variant(s).

Responsive design using screen variants

There’s a special global variant group called “Screen” variants, built in to all projects. These are treated specially because they can specify min and max viewport widths at which they kick in. For instance, if you’re doing mobile-first design but want to make adjustments for the desktop, you can create a screen variant called “Desktop” that activates on devices over 300px wide. Then, whenever you have an artboard that is over 300px, this Desktop variant will be automatically shown by default.

To learn more, see responsive design using screen variants.

Switching between global variants

While Plasmic Studio doesn’t support switching global variants out of the box, you can implement this functionality by setting up your app host to manage the variant state.

Prerequisites

This example requires setting up an app host connected to Plasmic Studio and basic knowledge of React concepts like context, hooks, and components.

As an example, we’ll build a dark mode switcher that toggles between light and dark themes. You can inspect the resulting demo and source code in this repository, or follow a step by step guide:

  1. Create a global variant group in Plasmic Studio called Theme, and two global variants named light and dark.

In order to do that go to any page or a component, and in the right sidebar select the Page (or Component) Data tab. Then scroll to the bottom to find the Global Variants section and hit the + button to create a new global variant group:

Global variants in Plasmic Studio
  1. Create a React context to manage the global variant state:
Copy
// contexts/GlobalVariantContext.tsx
// (optional) persist theme preference in cookies
import { getCookie, setCookie } from 'cookies-next';
import React from 'react';
// Cookie name for theme preference
const THEME_COOKIE_NAME = 'plasmic-theme';
interface GlobalVariantContextType {
isDarkMode: boolean;
toggleDarkMode: () => void;
}
const defaultContextValue: GlobalVariantContextType = {
isDarkMode: true, // Default to dark mode if not set
toggleDarkMode: () => {}
};
const GlobalVariantContext = React.createContext<GlobalVariantContextType>(defaultContextValue);
export function GlobalVariantProvider({ children }: { children: React.ReactNode }) {
// Load theme preference from cookie on mount
const [isDarkMode, setIsDarkMode] = React.useState(false);
React.useEffect(() => {
const savedTheme = getCookie(THEME_COOKIE_NAME);
setIsDarkMode(!savedTheme || savedTheme === 'dark');
}, []);
const toggleDarkMode = () => {
setIsDarkMode((prev) => {
// Save the new value to cookies
setCookie(THEME_COOKIE_NAME, !prev ? 'dark' : 'light');
return !prev;
});
};
return (
<GlobalVariantContext.Provider value={{ isDarkMode, toggleDarkMode }}>{children}</GlobalVariantContext.Provider>
);
}
export const useGlobalVariant = () => {
const context = React.useContext(GlobalVariantContext);
return context || defaultContextValue; // fallback inside Studio preview
};
  1. Pass the name and the active value of the variant that you want to change into the PlasmicRootProvider:
Copy
// components/PlasmicWrapper.tsx
import { PlasmicRootProvider } from '@plasmicapp/loader-nextjs';
import { useGlobalVariant } from '@/contexts/GlobalVariantContext';
type PlasmicWrapperProps = React.ComponentProps<typeof PlasmicRootProvider>;
export function PlasmicWrapper(props: PlasmicWrapperProps) {
const { children, ...rest } = props;
const { isDarkMode } = useGlobalVariant();
return (
<PlasmicRootProvider {...rest} globalVariants={[{ name: 'Theme', value: isDarkMode ? 'dark' : 'light' }]}>
{children}
</PlasmicRootProvider>
);
}
  1. Wrap your loader app with GlobalVariantProvider:
Copy
// pages/[[...catchall]].tsx
export default function PlasmicLoaderPage(props: {
plasmicData?: ComponentRenderData;
queryCache?: Record<string, unknown>;
}) {
const { plasmicData } = props;
if (!plasmicData || plasmicData.entryCompMetas.length === 0) {
return <Error statusCode={404} />;
}
const pageMeta = plasmicData.entryCompMetas[0];
return (
<GlobalVariantProvider>
<PlasmicWrapper>
<PlasmicComponent component={pageMeta.displayName} />
</PlasmicWrapper>
</GlobalVariantProvider>
);
}
  1. Create a wrapper component to pass the current state and actions to the Studio:
Copy
// components/DarkModeToggleWrapper.tsx
import React from 'react';
import { DataProvider } from '@plasmicapp/loader-nextjs';
import { useGlobalVariant } from '@/contexts/GlobalVariantContext';
interface DarkModeToggleWrapperProps {
children?: React.ReactNode;
}
interface DarkModeToggleWrapperActions {
toggleTheme(): void;
}
const DarkModeToggleWrapperBase = ({ children }: DarkModeToggleWrapperProps, ref: React.Ref<unknown>) => {
const { isDarkMode, toggleDarkMode } = useGlobalVariant();
React.useImperativeHandle(ref, () => ({
toggleTheme: toggleDarkMode
}));
return (
<DataProvider name="theme" data={{ isDarkMode }}>
{children}
</DataProvider>
);
};
export const DarkModeToggleWrapper = React.forwardRef<DarkModeToggleWrapperActions, DarkModeToggleWrapperProps>(
DarkModeToggleWrapperBase
);
  1. Register your code component to make it available in Plasmic Studio:
Copy
// plasmic-init.ts
PLASMIC.registerComponent(DarkModeToggleWrapper, {
name: 'DarkModeToggleWrapper',
props: {
children: 'slot'
},
providesData: true,
refActions: {
toggleTheme: {
argTypes: [],
displayName: 'Toggle dark mode',
description: 'Toggles between light and dark mode'
}
}
});
  1. Use the DarkModeToggleWrapper component in Studio to wrap the UI element that would act as a toggle (a Button, for example)

  2. Use your Theme global variant to style the button in the light and dark modes. Alternatively you can use the isDarkMode prop passed to the component.

  3. Add an onChange interaction to the UI element to call the Toggle dark mode element action.

Global variants in Plasmic Studio
Was this page helpful?

Have feedback on this page? Let us know on our forum.