@rayna2/shared-ui

v1.0.3

Published

Shared React UI component library for Rayna — booking flows, product cards, search, checkout, and more

Readme

@rayna2/shared-ui

A comprehensive React UI component library for the Rayna platform. Provides reusable components, hooks, utilities, and styling primitives shared across all Rayna applications (web-app, admin, and others).

Features

  • 50+ production-ready components — forms, tables, cards, navigation, modals, and more
  • Dual packaging — ships as both ESM (.js) and CommonJS (.cjs) for maximum compatibility
  • Tree-shakeable — import only what you need; unused code is eliminated during bundling
  • Next.js and generic React support — works seamlessly with Next.js App Router, Pages Router, and plain React (Vite, CRA, etc.)
  • Deep-path imports — import individual components directly for granular bundle control
  • First-class TypeScript — all components ship with complete type declarations
  • Source maps — included for debugging in development

Installation

npm install @rayna2/shared-ui
# or
yarn add @rayna2/shared-ui
# or
pnpm add @rayna2/shared-ui

Peer Dependencies

This package expects the following to be installed by the consumer project. They are not bundled to avoid duplicate copies in your application.

| Package | Minimum Version | |---|---| | react | ^18.0.0 | | react-dom | ^18.0.0 | | next | ^14.0.0 (optional — only for Next.js integration) | | @trpc/client | ^11.0.0 (optional — only for tRPC integration) | | @trpc/server | ^11.0.0 (optional) | | @trpc/react-query | ^11.0.0 (optional) |

Components that depend on tRPC will gracefully degrade or require minimal integration setup (see tRPC setup below).

Bundled Workspace Dependencies

The following internal packages are bundled into @rayna2/shared-ui so consumers don't need to install them separately:

  • @rayna2/icons — SVG icon components
  • @rayna2/shared-utils — general-purpose utilities
  • @rayna2/colors — design token color values

Setup

Next.js (App Router or Pages Router)

The library works out of the box with Next.js. No additional configuration is required.

For 'use client' components, Next.js will automatically handle client/server boundaries. The library's internal aliases map next/* imports to generic React wrappers when building for non-Next.js consumers, but when used inside a Next.js project, the native Next.js modules are used directly.

Generic React (Vite, Create React App, etc.)

When using this library outside of Next.js (plain React, Vite, etc.), the build automatically replaces next/* imports (like next/link, next/image, next/navigation) with lightweight, generic React-compatible stubs. No manual configuration is needed.

tRPC Integration

If your project uses tRPC, ensure the following peer dependencies are installed:

npm install @trpc/client @trpc/server @trpc/react-query

The library's tRPC-dependent hooks will pick up your project's tRPC client configuration automatically through React context.

Usage

Basic Component Import

import { Button } from '@rayna2/shared-ui';
import { Card } from '@rayna2/shared-ui';

function MyComponent() {
  return (
    <Card>
      <h2>Welcome</h2>
      <Button variant="primary" onClick={() => alert('Clicked!')}>
        Get Started
      </Button>
    </Card>
  );
}

Named Imports (Tree-shaking Friendly)

import { Button, Card, Modal, TextInput, Spinner } from '@rayna2/shared-ui';

Deep-path Imports (Granular Import Control)

For maximum tree-shaking, import components directly from their file path:

import { Button } from '@rayna2/shared-ui/Button';
import { ProductCard } from '@rayna2/shared-ui/new-components/ProductDisplay/ProductCardWrapper';

Note: Deep-path imports bypass the barrel file and may improve build performance for large applications. All publicly exported components are available both from the main entry point and via deep paths.

Hook Imports

import { useDebounce } from '@rayna2/shared-ui';
import { useMediaQuery } from '@rayna2/shared-ui';

function ResponsiveComponent() {
  const isMobile = useMediaQuery('(max-width: 768px)');
  const debouncedValue = useDebounce(searchTerm, 300);
  // ...
}

TypeScript

This library ships with complete TypeScript declarations. All components, hooks, and utilities export typed interfaces and type helpers.

Typed Components

import { TextInput, type TextInputProps } from '@rayna2/shared-ui';

function Form(): JSX.Element {
  const [value, setValue] = React.useState('');

  return (
    <TextInput
      label="Email"
      value={value}
      onChange={setValue}
      placeholder="Enter your email"
      error={!isValidEmail(value) ? 'Invalid email address' : undefined}
    />
  );
}

Typed Hooks

import { useAsyncData, type AsyncDataState } from '@rayna2/shared-ui';

interface User {
  id: string;
  name: string;
}

function UserProfile({ userId }: { userId: string }) {
  const { data, isLoading, error } = useAsyncData<User>(
    () => fetchUser(userId),
    [userId]
  );

  if (isLoading) return <Spinner />;
  if (error) return <ErrorDisplay message={error.message} />;
  return <div>{data?.name}</div>;
}

Building

If you are working on this library within the Rayna monorepo:

# Build the library
nx build shared-ui

# Watch mode for development
nx build shared-ui --watch

# Run tests
nx test shared-ui

# Run tests with coverage
nx test shared-ui --coverage

Output Structure

After building, the dist/ directory contains:

dist/
├── esm/              # ES module (.js) output
│   ├── Button.js
│   ├── Card.js
│   └── ...
├── cjs/              # CommonJS (.cjs) output
│   ├── Button.cjs
│   ├── Card.cjs
│   └── ...
├── Button.d.ts       # TypeScript declarations
├── Card.d.ts
└── ...

Contents

The library includes components across several categories:

| Category | Examples | |---|---| | Primitives | Button, TextInput, Card, Modal, Spinner, Badge, Tooltip | | Layout | Container, Grid, Stack, Divider, Flex | | Forms | FormField, Select, Checkbox, RadioGroup, DatePicker, FileUpload | | Data Display | Table, DataGrid, List, Avatar, Chip, Tag | | Navigation | Tabs, Breadcrumbs, Pagination, SideNav | | Feedback | Toast, Alert, ProgressBar, Skeleton, EmptyState | | Hooks | useDebounce, useMediaQuery, useAsyncData, useClickOutside, useLocalStorage | | New Components | ProductCard, ActivityBooking, SalesDashboard, CustomerSelector | | Utilities | formatCurrency, formatDate, cn (classnames), validators |

Troubleshooting

Module not found: Can't resolve '@rayna2/shared-ui'

Ensure the package is installed correctly:

npm ls @rayna2/shared-ui

If using a monorepo (pnpm workspaces, yarn workspaces), check that the package is listed in your workspace configuration.

'next' module not found errors

This library supports both Next.js and plain React. When building for a non-Next.js consumer, next/* modules are automatically aliased to generic stubs. If you see errors about missing next modules, ensure:

  1. The library was built with the correct tsup configuration (it handles aliasing automatically).
  2. If you are using Next.js, install it as a dependency:
    npm install next

Deep-path imports not resolving

Deep-path imports require the dist/ directory to maintain the same folder structure as src/. If imports like @rayna2/shared-ui/Button fail:

  1. Verify the library was built successfully (nx build shared-ui).
  2. Check that dist/ is present and contains the expected file structure.
  3. Ensure your bundler resolves subpath exports from package.json.

Tree-shaking not working

If you notice unused components being included in your bundle:

  1. Prefer named imports from the main entry point (import { Button } from '@rayna2/shared-ui').
  2. For optimal results, use deep-path imports (import { Button } from '@rayna2/shared-ui/Button').
  3. Verify your bundler is configured for tree-shaking (Webpack 5+, Vite, or esbuild handle this automatically).

CSS not loading

The library uses CSS files that are copied alongside the JavaScript output. If styles are missing:

  1. Ensure your bundler is configured to handle .css imports (Next.js and Vite do this automatically).
  2. If you are using a custom bundler, you may need to add a CSS loader.

Type errors in components

Ensure your tsconfig.json includes the following compiler options:

{
  "compilerOptions": {
    "jsx": "react-jsx",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "strict": true
  }
}

If using deep-path imports, also enable moduleResolution: "bundler" or "node16".

CommonJS vs ESM issues

This library ships both formats. Modern bundlers (Webpack 5, Vite, esbuild, Next.js) will automatically pick the ESM build. If you encounter issues:

  1. For ESM projects: the .js files in dist/esm/ are used automatically.
  2. For CJS projects: the .cjs files in dist/cjs/ are used automatically.
  3. If your bundler picks the wrong format, explicitly configure resolution in your bundler's config.

License

MIT — see LICENSE.