Back to Blog

Building reusable UI components: HeroUI v3 with TypeScript

UI Components
Design System
TypeScript
Next.js
Architecture

Building a scalable component architecture from scratch requires implementing component composition patterns, shared UI packages, design system architecture, type-safe wrappers, and custom component development. From HeroUI v3 integration to TypeScript type safety and component reusability, creating a production-ready UI system requires weeks of careful development. Fastack comes with a complete UI component library built on HeroUI v3 that handles component composition, type safety, and design system patterns—saving you weeks of development time.

Why component architecture matters for SaaS

A well-structured component architecture provides essential benefits:

  • Code reusability across applications
  • Consistent design system implementation
  • Type-safe component APIs with TypeScript
  • Easy maintenance and updates
  • Shared components across monorepo packages

What you get with Fastack

Fastack includes a complete UI component library built on HeroUI v3:

Shared UI package

Fastack provides a unified UI package that exports all components:

packages/ui-core/index.tsx
/**
 * @saas/ui-core - Unified UI Component Library
 * 
 * This package provides a unified interface for HeroUI components.
 * 
 * Usage:
 * import { Button, Input, Card, Modal, Tabs, Tab } from '@saas/ui-core';
 */

// Export components
export * from './components';

// Export HeroUI Providers directly
export { HeroUIProvider, ToastProvider } from '@heroui/react';

// Icons (library-agnostic)
export * from './icons';

The @saas/ui-core package provides a single import point for all UI components, making it easy to use components across the monorepo while maintaining type safety and consistency.

Type-safe component wrappers

Fastack wraps HeroUI components with type-safe interfaces:

packages/ui-core/components/button.tsx
'use client';

import { Button as HeroUIButton, ButtonProps as HeroUIButtonProps } from '@heroui/react';
import { forwardRef } from 'react';
import { cn } from '@saas/utils';

export interface ButtonProps extends HeroUIButtonProps {}

export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
  ({ className, children, isDisabled, ...props }, ref) => {
    return (
      <HeroUIButton
        ref={ref}
        className={cn(className)}
        isDisabled={isDisabled}
        {...props}
      >
        {children}
      </HeroUIButton>
    );
  }
);

Button.displayName = 'Button';

Component wrappers extend HeroUI props with TypeScript interfaces, ensuring type safety while allowing customization. The cn utility merges Tailwind classes properly, and forwardRef ensures proper ref forwarding.

Compound component patterns

Fastack uses compound component patterns for complex components:

packages/ui-core/components/modal.tsx
'use client';

import {
  Modal as HeroUIModal,
  ModalProps as HeroUIModalProps,
  ModalContent,
  ModalHeader,
  ModalBody,
  ModalFooter,
} from '@heroui/react';
import { forwardRef } from 'react';
import { cn } from '@saas/utils';

export type ModalProps = HeroUIModalProps;

export const Modal = forwardRef<HTMLDivElement, ModalProps>(
  ({ className, ...props }, ref) => {
    return (
      <HeroUIModal
        placement="center"
        ref={ref}
        className={cn(className)}
        {...props}
      />
    );
  }
);

Modal.displayName = 'Modal';

// Sub-components
export { ModalContent, ModalHeader, ModalBody, ModalFooter };

Compound components allow flexible composition. The Modal component exports sub-components that can be used together, providing a clean API while maintaining type safety.

Custom component variants

Fastack includes custom components with predefined variants:

packages/ui-core/components/alert.tsx
export interface ErrorAlertProps {
  message?: string;
  title?: ReactNode;
  description?: ReactNode;
  className?: string;
  isClosable?: boolean;
  onClose?: () => void;
  children?: ReactNode;
}

export function ErrorAlert({
  message,
  title,
  description,
  className,
  isClosable,
  onClose,
  children,
}: ErrorAlertProps) {
  return (
    <Alert
      color="danger"
      variant="flat"
      title={title}
      description={description || message || children}
      className={className}
      isClosable={isClosable}
      onClose={onClose}
    />
  );
}

Custom component variants like ErrorAlert and SuccessAlert provide convenient shortcuts for common use cases while maintaining the flexibility of the base component.

Complex custom components

Fastack includes complex custom components like code blocks:

packages/ui-core/components/code-block.tsx
export const CodeBlockCode = forwardRef<HTMLDivElement, CodeBlockCodeProps>(
  (
    {
      code,
      language = 'text',
      theme = 'github-dark',
      className,
      showCopyButton = true,
      ...props
    },
    ref
  ) => {
    const [highlightedHtml, setHighlightedHtml] = useState<string | null>(null);
    const [copied, setCopied] = useState(false);

    useEffect(() => {
      let isMounted = true;

      async function highlight() {
        if (!code) {
          setHighlightedHtml('<pre><code></code></pre>');
          return;
        }

        try {
          const shiki = await import('shiki');
          const html = await shiki.codeToHtml(code, { lang: language, theme });
          if (isMounted) {
            setHighlightedHtml(html);
          }
        } catch (error) {
          console.error('Failed to highlight code:', error);
          if (isMounted) {
            setHighlightedHtml(`<pre><code class="language-${language}">${code}</code></pre>`);
          }
        }
      }
      highlight();

      return () => {
        isMounted = false;
      };
    }, [code, language, theme]);

    const handleCopy = async () => {
      try {
        await navigator.clipboard.writeText(code);
        setCopied(true);
        setTimeout(() => setCopied(false), 2000);
      } catch (err) {
        console.error('Failed to copy:', err);
      }
    };

    // ... render logic with syntax highlighting and copy button
  }
);

Complex components like CodeBlock demonstrate advanced patterns including async code highlighting, copy-to-clipboard functionality, and proper cleanup with useEffect.

Component usage examples

Fastack demonstrates component usage in real applications:

apps/fastack-boilerplate/src/components/landing/landing-faq.tsx
'use client';

import { Accordion, AccordionItem } from '@saas/ui-core';
import { useClientTranslations } from '@/i18n/client';
import { ScrollAnimate } from '@saas/utils';

export function LandingFaq() {
  const t = useClientTranslations('landing');

  const faqItems = ['1', '2', '3', '4', '5', '6'];

  return (
    <section id="faq" className="py-20 px-4 w-full bg-backgroundsection">
      <div className="max-w-4xl mx-auto">
        <ScrollAnimate animation="fadeInUp" className="text-center mb-16">
          <h2 className="text-3xl sm:text-4xl font-bold text-foreground mb-4">
            {t('faq.title')}
          </h2>
          <p className="text-lg text-foreground/70 max-w-2xl mx-auto">
            {t('faq.subtitle')}
          </p>
        </ScrollAnimate>
        <ScrollAnimate animation="fadeInUp" delay={200}>
          <Accordion variant="splitted" className="w-full">
            {faqItems.map((key) => (
              <AccordionItem
                key={key}
                aria-label={t(`faq.items.${key}.question`)}
                title={t(`faq.items.${key}.question`)}
              >
                <p className="text-foreground/70">
                  {t(`faq.items.${key}.answer`)}
                </p>
              </AccordionItem>
            ))}
          </Accordion>
        </ScrollAnimate>
      </div>
    </section>
  );
}

Components from @saas/ui-core are used throughout the application, demonstrating real-world usage patterns with i18n integration and responsive design.

Available components

Fastack's @saas/ui-core package includes 60+ production-ready components, all available from a single import:

packages/ui-core/components/index.tsx
import { Button, Input, Card, Modal, Tabs, Tab } from '@saas/ui-core';

Form components

Button
Input
Textarea
Select
Autocomplete
Checkbox
RadioGroup
Switch
InputOtp
InputDate
InputTime
InputNumber
Slider

Layout & navigation

Card
Modal
Tabs
Accordion
Sidebar
Navbar
Dropdown
Pagination
Table
ScrollShadow

Feedback & display

Alert
Spinner
Progress
Skeleton
Badge
Chip
Tooltip
Avatar
Image
ImageZoom

Content & media

Code
CodeBlock
Link
Kbd
Carousel
Calendar
TableOfContents

Background & effects

DotPattern
Particles
BackgroundGrid
FloatingLines
LightPillar
Marquee
BorderBeam
AuroraText
AvatarCircles
Confetti

Utilities & hooks

ThemeProvider
ThemeToggle
Search
useDisclosure
useToast
useConfetti
Motion components

All components are fully typed with TypeScript, support dark mode, and follow HeroUI v3 design patterns. Each component can be imported individually or as part of compound component patterns for maximum flexibility.

Time saved: 20+ hours

Building a reusable UI component library from scratch typically requires:

  • Setting up component package structure (2-3 hours)
  • Creating type-safe wrappers (4-5 hours)
  • Implementing compound components (3-4 hours)
  • Building custom component variants (3-4 hours)
  • Creating complex components (4-5 hours)
  • Testing component integration (2-3 hours)
  • Documenting component API (2-3 hours)

Total: 20-27 hours of development time that you save with Fastack.

Best practices included

Fastack follows component architecture best practices:

  • Type-safe wrappers - Full TypeScript support
  • Compound components - Flexible composition patterns
  • Shared package - Reusable across monorepo
  • Custom variants - Convenient shortcuts for common cases
  • Design system - Consistent styling and behavior

Conclusion

A well-structured component architecture is essential for scalable SaaS applications. Fastack provides a complete UI component library built on HeroUI v3 that saves you weeks of development time while ensuring type safety and design consistency.

With Fastack, you get:

  • Shared UI package with unified component exports
  • Type-safe component wrappers with full TypeScript support
  • Compound component patterns for flexible composition
  • Custom component variants for common use cases
  • Complex custom components with advanced functionality
  • Design system architecture built-in from day one

Ready to build your SaaS faster?

Get our scalable, production-ready boilerplate to save endless hours of development and setup

Building reusable UI components: HeroUI v3 with TypeScript - Fastack