LogoShip Superfast

Components

shadcn/ui components, providers, and the cn() utility.

shadcn/ui

The web app uses shadcn/ui for UI components. These are not imported from a package — they live as source files in your project at components/ui/. You own the code and can modify it directly.

Configuration

The shadcn config is in components.json:

{
  "style": "radix-maia",
  "rsc": true,
  "tsx": true,
  "iconLibrary": "hugeicons",
  "tailwind": {
    "css": "app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true
  }
}

Key settings:

  • Style: radix-maia — a modern preset from shadcn
  • Icons: Hugeicons (not Lucide) — used throughout the app
  • RSC: React Server Components enabled
  • Registry: Also includes @react-bits for extra components

Installed components

There are 55 components installed in components/ui/:

accordion, alert, alert-dialog, aspect-ratio, avatar, badge, breadcrumb, button, button-group, calendar, card, carousel, chart, checkbox, collapsible, combobox, command, context-menu, dialog, direction, drawer, dropdown-menu, empty, field, hover-card, input, input-group, input-otp, item, kbd, label, menubar, native-select, navigation-menu, pagination, popover, progress, radio-group, resizable, scroll-area, select, separator, sheet, sidebar, skeleton, slider, sonner, spinner, switch, table, tabs, textarea, toggle, toggle-group, tooltip

Adding new components

cd apps/web
pnpm dlx shadcn@latest add <component-name>

This downloads the component source into components/ui/.

The cn() utility

Located at lib/utils.ts, this is a helper for merging Tailwind class names:

import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

Use it when you need to conditionally combine classes:

<div className={cn("rounded-lg p-4", isActive && "bg-primary text-white")} />

twMerge handles conflicts — if two classes target the same property (like p-4 and p-6), the last one wins.

Providers

The app uses four context providers, all in components/providers/:

ConvexClientProvider

Connects the app to the Convex backend. Wraps the entire app.

import { ConvexReactClient, ConvexProvider } from "convex/react";
import { ConvexAuthProvider } from "@convex-dev/auth/react";

Gives every component access to useQuery(), useMutation(), and useAction() for talking to the backend.

SessionProvider

Provides the current user and auth state. See Authentication for details.

const { currentUser, isSignedIn, isLoading, isAdmin, signOut } = useSession();

TeamProvider

Manages the active team. The user can be a member of multiple teams and switch between them.

const { activeTeam, teams, isLoading, switchTeam } = useTeam();
PropertyTypeDescription
activeTeamTeam | nullThe currently selected team
teamsTeam[]All teams the user belongs to
isLoadingbooleantrue while fetching teams
switchTeam(teamId) => voidSwitch to a different team

The active team is persisted in localStorage so it survives page refreshes.

ThemeProvider

Handles light/dark mode. See Theming for details.

Icons

The app uses Hugeicons instead of Lucide. Import icons like this:

import { HugeiconsIcon } from "@hugeicons/react";
import { Mail01Icon } from "@hugeicons/core-free-icons";

<HugeiconsIcon icon={Mail01Icon} className="h-5 w-5" />

Browse available icons at hugeicons.com.

Key files

FilePurpose
components/ui/All 55 shadcn/ui components
components/providers/Convex, session, team, theme providers
components.jsonshadcn/ui configuration
lib/utils.tscn() class name utility

On this page