Skip to main content

Components

The UniPulse UI is built with shadcn/ui components on top of Radix UI primitives, styled with Tailwind CSS v4. Components follow a composition pattern with consistent styling via the cn() utility.


Component Architecture


Layout Components

ComponentPurposeUsed In
AppLayoutMain authenticated layout with sidebar + header + workspace selectorAll authenticated pages
AuthLayoutLogin/register layout (centered card, branding)/login, /register
SettingsLayoutSettings page with sub-navigation tabs/settings/*
AdminLayoutAdmin panel layout with admin-specific sidebar/admin/*

AppLayout Structure

+------+---------------------------+
| | Header (workspace, user) |
| Side |---------------------------+
| bar | |
| | Page Content |
| | (children) |
| | |
+------+---------------------------+

Data Display Components

ComponentDescriptionKey Props
DataTableSortable, filterable tables with paginationcolumns, data, searchable, sortable
MetricCardKPI display cards with trend indicatorstitle, value, trend, icon
ChartRecharts wrapper for time-series and comparison chartstype, data, xKey, yKey
PlatformBadgeShows social platform icon + nameplatform
StatusBadgeColored status indicatorstatus, variant
EmptyStatePlaceholder for empty data viewstitle, description, action

Form Components

Forms use React Hook Form with Zod validation from @unipulse/shared:

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { createPostSchema, type CreatePostInput } from '@unipulse/shared';
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage } from '@/components/ui/form';

function CreatePostForm() {
const form = useForm<CreatePostInput>({
resolver: zodResolver(createPostSchema),
defaultValues: { caption: '', platforms: [] },
});

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField control={form.control} name="caption" render={({ field }) => (
<FormItem>
<FormLabel>{t('composer.caption')}</FormLabel>
<FormControl><Textarea {...field} /></FormControl>
<FormMessage />
</FormItem>
)} />
</form>
</Form>
);
}

Workflow Builder

The visual workflow editor is built with @xyflow/react (React Flow):

Custom NodePurposeVisual
TriggerNodeEvent that starts the workflow (e.g., new post, schedule)Green circle
ConditionNodeEvaluates a boolean expression (eq, gt, contains, etc.)Yellow diamond
ActionNodePerforms an operation (notify, publish, reply, boost, etc.)Blue rectangle

Features:

  • Drag-and-drop from a sidebar palette
  • Node configuration panels (slide-out form)
  • Edge connections with animated flow indicators
  • Undo/redo support
  • Test run execution from the editor

Composer Component

The multi-platform composer is one of the most complex components:

Sub-ComponentPurpose
CaptionEditorRich text editor for post caption
PlatformSelectorToggle target platforms (Facebook, Instagram, TikTok)
MediaUploaderDrag-and-drop media attachment
PlatformPreviewLive preview of how the post will look on each platform
AIAssistantAI caption generation, rewriting, hashtags sidebar
SchedulePickerDate/time picker for scheduling
BrandVoiceSelectorApply brand voice to AI-generated content

Adding New shadcn/ui Components

cd apps/web
npx shadcn@latest add button
npx shadcn@latest add dialog
npx shadcn@latest add dropdown-menu

Components are added to apps/web/src/components/ui/ and can be customized freely.


Styling Conventions

ConventionExample
Tailwind utility classesclassName="flex items-center gap-2 p-4"
Conditional classes with cn()cn("text-sm", isActive && "font-bold")
Component variantsUse cva() for variant props (shadcn pattern)
Dark modeSupports via Tailwind dark: prefix
RTL supportTailwind v4 logical properties auto-handle RTL
ResponsiveMobile-first with sm:, md:, lg: breakpoints

cn() Helper

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

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

Icon Usage

All icons come from Lucide React:

import { Plus, Trash2, Settings, BarChart3 } from 'lucide-react';

<Button><Plus className="h-4 w-4 mr-2" /> Create Post</Button>

Cross-Reference