Tabs

Tabs component is used to display a set of tabs that can be used to navigate between different sections of a page.

Login content
export const Example = () => {
  return (
    <Tabs defaultValue="login">
      <TabsList>
        <TabsTrigger value="login">Login</TabsTrigger>
        <TabsTrigger value="register">Register</TabsTrigger>
      </TabsList>
      <TabsContent value="login" className="mt-2">
        <div>Login content</div>
      </TabsContent>
      <TabsContent value="register" className="mt-2">
        <div>Register content</div>
      </TabsContent>
    </Tabs>
  );
};

Installation

If you haven't included Piksel UI in your project, include it first. You can check the installation page.

Source code

Copy the following source code and add it to your project.

"use client";

import { cn } from "@/utils/cn";
import * as TabsPrimitive from "@radix-ui/react-tabs";
import {
  ComponentPropsWithRef,
  createContext,
  forwardRef,
  useContext,
  useEffect,
} from "react";
import { tv, VariantProps } from "tailwind-variants";

type TabsContextType = VariantProps<typeof tabsStyles>;
const TabsContext = createContext<TabsContextType | null>(null);
const TabsContextProvider = TabsContext.Provider;
const useTabsContext = () => {
  const context = useContext(TabsContext) || {};
  return context;
};

export const tabsDisplayName = "Tabs";
export type TabsProps = ComponentPropsWithRef<typeof TabsPrimitive.Root> &
  VariantProps<typeof tabsStyles>;
/**
 * @name Tabs
 * @description Tabs component is used to display a set of tabs that can be used to navigate between different sections of a page.
 */
export const Tabs = forwardRef<HTMLDivElement, TabsProps>((props, ref) => {
  const { variant, color, size, radius, className, ...rest } = props;

  const { root } = tabsStyles({
    variant,
    color,
    size,
    radius,
  });

  return (
    <TabsContextProvider
      value={{
        variant,
        color,
        size,
        radius,
      }}
    >
      <TabsPrimitive.Root className={root({ className })} {...rest} ref={ref} />
    </TabsContextProvider>
  );
});
Tabs.displayName = tabsDisplayName;

export const tabsListDisplayName = "TabsList";
export type TabsListProps = ComponentPropsWithRef<typeof TabsPrimitive.List>;
/**
 * @name TabsList
 * @description TabsList component is used to display a list of tabs that can be used to navigate between different sections of a page.
 */
export const TabsList = forwardRef<HTMLDivElement, TabsListProps>(
  (props, ref) => {
    const { className, ...rest } = props;
    const context = useTabsContext();

    const { list } = tabsStyles(context);

    useEffect(() => {}, [rest.id]);
    return (
      <TabsPrimitive.List className={list({ className })} {...rest} ref={ref} />
    );
  }
);
TabsList.displayName = tabsListDisplayName;

export const tabsTriggerDisplayName = "TabsTrigger";
export type TabsTriggerProps = ComponentPropsWithRef<
  typeof TabsPrimitive.Trigger
>;
/**
 * @name TabsTrigger
 * @description TabsTrigger component is used to display a trigger
 */
export const TabsTrigger = forwardRef<HTMLButtonElement, TabsTriggerProps>(
  (props, ref) => {
    const { className, ...rest } = props;
    const context = useTabsContext();
    const { trigger } = tabsStyles(context);

    return (
      <TabsPrimitive.Trigger
        className={trigger({ className })}
        {...rest}
        ref={ref}
      />
    );
  }
);
TabsTrigger.displayName = tabsTriggerDisplayName;

export const tabsContentDisplayName = "TabsContent";
export type TabsContentProps = ComponentPropsWithRef<
  typeof TabsPrimitive.Content
>;
/**
 * @name TabsContent
 * @description TabsContent component is used to display the content of the tabs
 */
export const TabsContent = forwardRef<HTMLDivElement, TabsContentProps>(
  (props, ref) => {
    const { className, ...rest } = props;
    const context = useTabsContext();
    const { content } = tabsStyles(context);

    return (
      <TabsPrimitive.Content
        className={cn(content({ className }))}
        {...rest}
        ref={ref}
      />
    );
  }
);
TabsContent.displayName = tabsContentDisplayName;

export const tabsStyles = tv({
  /**
   * Base styles of the tabs
   */
  slots: {
    root: "flex flex-col",
    list: "flex items-stretch overflow-x-auto",
    trigger: [
      "flex-1 text-center cursor-pointer whitespace-nowrap transition-colors",
      "data-[state=active]:shadow disabled:cursor-not-allowed disabled:opacity-75",
    ],
    content: "",
  },
  variants: {
    /**
     * Variant prop of the tabs
     */
    variant: {
      filled: {},
      soft: {},
      outline: {},
    },
    /**
     * Color prop of the tabs
     */
    color: {
      blue: {},
      red: {},
      indigo: {},
      green: {},
      yellow: {},
      zinc: {},
      white: {},
      black: {},
    },
    /**
     * Size prop of the tabs
     */
    size: {
      sm: {
        list: "gap-0.5",
      },
      md: {
        list: "gap-1 p-0.5",
        trigger: "px-2 py-1 text-base",
      },
      lg: {
        list: "gap-1.5",
      },
    },
    radius: {
      none: {},
      sm: {},
      md: {
        list: "rounded-lg",
        trigger: "rounded-md",
      },
      lg: {},
      xl: {},
    },
  },
  compoundVariants: [
    /* -------------------------------------------------------------------------- */
    /*                             Variants and colors                            */
    /* -------------------------------------------------------------------------- */
    /* -------------------------------------------------------------------------- */
    /*                               Filled variant                               */
    /* -------------------------------------------------------------------------- */
    {
      variant: "filled",
      color: "blue",
      className: { root: "bg-blue-500 text-white hover:text-white" },
    },
    {
      variant: "filled",
      color: "red",
      className: { root: "bg-red-500 text-white hover:text-white" },
    },
    {
      variant: "filled",
      color: "indigo",
      className: { root: "bg-indigo-500 text-white hover:text-white" },
    },
    {
      variant: "filled",
      color: "green",
      className: { root: "bg-green-500 text-white hover:text-white" },
    },
    {
      variant: "filled",
      color: "yellow",
      className: { root: "bg-yellow-400 text-black hover:text-black" },
    },
    {
      variant: "filled",
      color: "zinc",
      className: { root: "bg-zinc-500 text-white hover:text-white" },
    },
    {
      variant: "filled",
      color: "white",
      className: {
        list: "bg-white shadow border",
        trigger:
          "font-medium text-zinc-900 data-[state=active]:bg-zinc-800 data-[state=active]:text-zinc-50",
      },
    },
    {
      variant: "filled",
      color: "black",
      className: {
        root: "bg-zinc-950 border-zinc-900 hover:text-white",
      },
    },
    /* -------------------------------------------------------------------------- */
    /*                                Soft variant                                */
    /* -------------------------------------------------------------------------- */
    {
      variant: "soft",
      color: "blue",
      className: { root: "bg-blue-100 text-blue-600 hover:text-blue-600" },
    },
    {
      variant: "soft",
      color: "red",
      className: { root: "bg-red-100 text-red-600 hover:text-red-600" },
    },
    {
      variant: "soft",
      color: "indigo",
      className: {
        root: "bg-indigo-100 text-indigo-600 hover:text-indigo-600",
      },
    },
    {
      variant: "soft",
      color: "green",
      className: { root: "bg-green-100 text-green-600 hover:text-green-600" },
    },
    {
      variant: "soft",
      color: "yellow",
      className: {
        root: "bg-yellow-100 text-yellow-600 hover:text-yellow-600",
      },
    },
    {
      variant: "soft",
      color: "zinc",
      className: { root: "bg-zinc-100 text-zinc-600 hover:text-zinc-600" },
    },
    {
      variant: "soft",
      color: "white",
      className: { root: "bg-zinc-50 text-zinc-950" },
    },
    {
      variant: "soft",
      color: "black",
      className: {
        root: "bg-zinc-200 text-zinc-600",
        tabsTitle: "text-zinc-950",
      },
    },
    /* -------------------------------------------------------------------------- */
    /*                               Outline variant                              */
    /* -------------------------------------------------------------------------- */
    {
      variant: "outline",
      color: "blue",
      className: {
        root: [
          "text-blue-500 outline-blue-500",
          "hover:bg-blue-500 hover:text-white",
          "focus-visible:ring-offset-blue-300",
        ],
      },
    },
    {
      variant: "outline",
      color: "red",
      className: {
        root: [
          "text-red-500 outline-red-500",
          "hover:bg-red-500 hover:text-white",
          "focus-visible:ring-offset-red-300",
        ],
      },
    },
    {
      variant: "outline",
      color: "indigo",
      className: {
        root: [
          "text-indigo-500 outline-indigo-500",
          "hover:bg-indigo-500 hover:text-white",
          "focus-visible:ring-offset-indigo-300",
        ],
      },
    },
    {
      variant: "outline",
      color: "green",
      className: {
        root: [
          "text-green-500 outline-green-500",
          "hover:bg-green-500 hover:text-white",
          "focus-visible:ring-offset-green-300",
        ],
      },
    },
    {
      variant: "outline",
      color: "yellow",
      className: {
        root: [
          "text-yellow-600",
          "hover:bg-yellow-500 hover:text-black",
          "focus-visible:ring-offset-yellow-300",
        ],
      },
    },
    {
      variant: "outline",
      color: "zinc",
      className: {
        root: [
          "text-zinc-500",
          "hover:bg-zinc-500 hover:text-white",
          "focus-visible:ring-offset-zinc-300",
        ],
      },
    },
    {
      variant: "outline",
      color: "white",
      className: {
        root: [""],
      },
    },
    {
      variant: "outline",
      color: "black",
      className: {
        root: ["text-zinc-950"],
      },
    },
  ],
  defaultVariants: {
    variant: "filled",
    color: "white",
    size: "md",
    radius: "md",
  },
});

Props

The Tabs component has the following props.

PropTypeDefaultDescription
variantfilled | soft | outline | filledfilledThe variant of the tabs.
colorblue | red | indigo | green | yellow | zinc | white | blackwhiteThe color of the tabs.
sizesm | md | lgmdThe size of the tabs.
radiusnone | sm | md | lg | xlmdThe radius of the tabs.