npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@tpzdsp/next-toolkit

v1.10.0

Published

A reusable React component library for Next.js applications

Readme

Next.js Component Library

A modern, reusable React component library designed specifically for Next.js applications. This library provides a collection of client and server components, hooks, contexts, and utilities with full TypeScript support.

📋 Prerequisites

  • Node.js: 22.17.1 (managed with asdf)
  • Package Manager: Yarn (preferred) or npm
  • Next.js: 13+ (peer dependency)
  • React: 18+ (peer dependency)

Features

  • 🚀 Next.js Optimized: Built specifically for Next.js with support for both client and server components
  • 🎨 Tailwind CSS v3: Styled with Tailwind CSS and includes tailwind-merge for efficient class merging
  • 📦 Multiple Export Patterns: Separate exports for client, server, and shared utilities
  • 🧪 Fully Tested: Comprehensive test coverage with Vitest and React Testing Library
  • 📝 TypeScript First: Full TypeScript support with proper type definitions
  • Fast Development: Built with Vite for lightning-fast development and builds
  • 🎯 Tree Shakeable: Optimized for tree-shaking to reduce bundle size
  • 🔄 Source Distribution: Distributes raw TypeScript files for optimal bundling

Installation

yarn add @your-org/nextjs-library
# or
npm install @your-org/nextjs-library
# or
pnpm add @your-org/nextjs-library

Usage

Basic Import

// Import everything
import { Button, Card, useLocalStorage } from '@your-org/nextjs-library';

// Import styles (choose one method)
import '@your-org/nextjs-library/styles';
// OR if the above doesn't work:
// import '@your-org/nextjs-library/src/assets/styles/globals.css';

// If you're using map components, also import OpenLayers styles:
import '@your-org/nextjs-library/styles/ol';

Selective Imports (Recommended)

// Import only client components
import { Button, NextLinkWrapper } from '@your-org/nextjs-library/client';

// Import only server components
import { Card, Container } from '@your-org/nextjs-library/server';

// Import components with heavy dependencies separately
import { Select } from '@your-org/nextjs-library/components/select';

// Import only types
import type { ButtonProps, ApiResponse, FormFieldProps } from '@your-org/nextjs-library/types';

Example Usage

import { Button } from '@your-org/nextjs-library/client';
import { Card } from '@your-org/nextjs-library/server';
import '@your-org/nextjs-library/styles'; // Base styles
// Import map styles only if you're using map components:
// import '@your-org/nextjs-library/styles/ol';

export default function MyPage() {
  return (
    <Card title="Welcome" description="Get started with our components">
      <Button variant="primary" size="lg">
        Get Started
      </Button>
    </Card>
  );
}

Using Exported Types

import type {
  ButtonProps,
  ButtonVariant,
  CardProps,
  BaseProps,
  ThemeContextValue,
} from '@your-org/nextjs-library';

// Use library types in your own components
interface MyCustomButtonProps extends ButtonProps {
  icon?: React.ReactNode;
}

// Use type unions for consistency
const handleVariantChange = (variant: ButtonVariant) => {
  console.log('Variant changed to:', variant);
};

// Extend base props for your own components
interface MyComponentProps extends BaseProps {
  customProp: string;
}

Available Components

Client Components

  • Button: Interactive button with multiple variants and loading states
  • NextLinkWrapper: Enhanced Next.js Link wrapper with external link handling
  • ThemeProvider: Theme context provider for dark/light mode

Server Components

  • Card: Flexible card component with title and description
  • Container: Responsive container with multiple size options

Hooks

  • useLocalStorage: localStorage hook with SSR support
  • useDebounce: Debounce values and functions

Map Components

  • Map: OpenLayers-based interactive map component
  • MapContext: React context for map state management
  • Popup: Map popup component for displaying information
  • LayerSwitcherControl: Control for switching between map layers

Note: Map components require additional dependencies and styles:

# Install OpenLayers dependencies
npm install ol ol-geocoder ol-mapbox-style proj4
// Import map styles when using map components
import '@your-org/nextjs-library/styles/ol';

Components with Optional Dependencies

Some components require additional peer dependencies that are marked as optional:

  • Select components: Require react-select
  • Map components: Require OpenLayers packages (ol, ol-geocoder, etc.)
# For Select components
npm install react-select

# For Map components  
npm install ol ol-geocoder ol-mapbox-style proj4 @turf/turf geojson
// Import Select components separately to avoid forcing dependency
import { Select } from '@your-org/nextjs-library/components/select';

Utilities

  • cn(): Tailwind class merging utility using tailwind-merge
  • debounce(): Function debouncing utility
  • formatBytes(): Human-readable byte formatting

Types & Interfaces

  • Component Props: ButtonProps, CardProps, ContainerProps, etc.
  • Common Types: BaseProps, Variant, Size, Status, Theme
  • Form Types: FormFieldProps, ChangeHandler, SubmitHandler
  • API Types: ApiResponse<T>, PaginatedResponse<T>, PaginationMeta
  • Utility Types: PropsWithRequired<T, K>, PropsWithOptional<T, K>

🏗️ Project Structure

src/
├── components/
│   ├── client/          # Client components ('use client' directive)
│   │   ├── Button/
│   │   │   ├── Button.tsx
│   │   │   └── Button.test.tsx
│   │   ├── NextLinkWrapper/
│   │   └── ThemeProvider/
│   └── server/          # Server components (SSR compatible)
│       ├── Card/
│       │   ├── Card.tsx
│       │   └── Card.test.tsx (when created)
│       └── Container/
├── hooks/               # React hooks (client-side only)
│   ├── useLocalStorage.ts
│   └── useDebounce.ts
├── contexts/            # React contexts (client-side only)
│   └── ThemeContext.tsx
├── utils/               # Utilities (client & server compatible)
│   └── index.ts
├── assets/              # CSS, images, and static assets
│   ├── styles/
│   └── images/
├── types.ts             # TypeScript type definitions
└── index.ts             # Main library exports

vitest.setup.ts          # Test configuration (top-level)

🧭 Architecture Decisions

Client vs Server Components

  • Client Components (src/components/client/): Include 'use client'; directive, used for interactive features, hooks, and state management
  • Server Components (src/components/server/): No directive needed, can be server-side rendered, used for static content and layouts

Source Distribution Strategy

This library distributes raw TypeScript/TSX files instead of compiled bundles:

  • ✅ Better tree-shaking in consuming applications
  • ✅ Consuming apps control the build process and browser targets
  • ✅ Smaller final bundle sizes
  • ✅ No build step needed during development

Styling Approach

  • Tailwind CSS v3 for utility-first styling
  • tailwind-merge (cn() utility) for intelligent class merging
  • Component-specific CSS classes in assets/styles/globals.css

Development

# Install dependencies
yarn install

# Start development server
yarn dev

# Run tests
yarn test

# Run tests with UI
yarn test:ui

# Build the library (TypeScript check only)
yarn build

# Build distribution files (if needed)
yarn build:dist

# Preview the built library
yarn preview

Local Development with Yalc

This library works well with yalc for local testing before publishing to npm. However, hot reloading doesn't work reliably with yalc - you'll need to manually push changes and restart your development server.

Setup Yalc (one-time)

# Install yalc globally
npm install -g yalc

Publishing to Yalc Store

# In this library directory
yalc publish

Using in Your Next.js App

# In your Next.js app directory
yalc add @tpzdsp/next-toolkit

# Install dependencies
yarn install

Your Next.js App Configuration

Add to your next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['@tpzdsp/next-toolkit'],
  experimental: {
    externalDir: true, // May help with yalc symlinks
  },
};

module.exports = nextConfig;

Development Workflow (Manual Process)

# 1. Make changes to the library components/hooks/etc.

# 2. Push changes to connected apps
yalc push

# 3. In your test app, clear Next.js cache and restart
rm -rf .next && yarn dev

Why No Hot Reloading?

  • Symlink Issues: Next.js file watching doesn't reliably detect changes in yalc-linked packages
  • Module Resolution: TypeScript and bundler caching can prevent updates from being picked up
  • Source Distribution: While our source distribution strategy helps with tree-shaking, it doesn't solve the hot reload problem with yalc

Removing from Test App

# In your Next.js app directory
yalc remove @tpzdsp/next-toolkit
yarn install

Benefits Despite Manual Process

  • Real Environment Testing: Test in an actual Next.js app setup
  • Full TypeScript: Complete type checking and IntelliSense
  • Tree Shaking: Your app's bundler handles optimization
  • No Publishing: Test without polluting npm registry
  • Manual Refresh Required: No automatic hot reloading

🛠️ Adding New Components

Client Component Template

// src/components/client/MyComponent/MyComponent.tsx
'use client';

import React from 'react';
import { cn } from '../../../utils';

export interface MyComponentProps {
  children: React.ReactNode;
  className?: string;
  // Add your props here
}

export const MyComponent: React.FC<MyComponentProps> = ({ children, className, ...props }) => {
  return (
    <div className={cn('base-classes', className)} {...props}>
      {children}
    </div>
  );
};

Server Component Template

// src/components/server/MyComponent/MyComponent.tsx
import React from 'react';
import { cn } from '../../../utils';

export interface MyComponentProps {
  children: React.ReactNode;
  className?: string;
  // Add your props here
}

export const MyComponent: React.FC<MyComponentProps> = ({ children, className, ...props }) => {
  return (
    <div className={cn('base-classes', className)} {...props}>
      {children}
    </div>
  );
};

Don't Forget To:

  1. Add exports to the appropriate index.ts file
  2. Create tests co-located with your component (e.g., MyComponent.test.tsx)
  3. Update TypeScript types if needed
  4. Test with yalc in a real Next.js app

🐛 Troubleshooting

Common Issues

"Cannot resolve module" errors:

# Make sure transpilePackages is configured in next.config.js
transpilePackages: ['@your-org/nextjs-library']

TypeScript errors in consuming app:

# Ensure your tsconfig.json includes:
{
  "compilerOptions": {
    "moduleResolution": "bundler", // or "node16"
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true
  }
}

Tailwind styles not working:

# Import the library styles in your app:
import '@your-org/nextjs-library/styles';

# Or alternatively, import the CSS file directly:
import '@your-org/nextjs-library/src/assets/styles/globals.css';

# And include the library in your tailwind.config.js content:
content: [
  // your paths...
  './node_modules/@your-org/nextjs-library/src/**/*.{js,ts,jsx,tsx}',
]

yalc issues:

# Reset yalc completely
yalc remove --all
yalc publish
yalc add @your-org/nextjs-library

Optional dependency errors:

# Error: Cannot resolve module 'react-select'
# Solution: Install the required dependency
npm install react-select

# Error: Cannot resolve module 'ol/ol.css' 
# Solution: Install OpenLayers dependencies
npm install ol ol-geocoder ol-mapbox-style proj4

# Or import components selectively to avoid these dependencies:
import { Button, Card } from '@your-org/nextjs-library'; // ✅ No extra deps needed
import { Select } from '@your-org/nextjs-library/components/select'; // ❌ Requires react-select

Testing

This library uses Vitest and React Testing Library for testing:

# Run all tests
yarn test

# Run tests in watch mode
yarn test:watch

# Run tests with coverage
yarn test:coverage

Build Configuration

The library distributes raw TypeScript/TSX source files rather than pre-built bundles. This approach offers several benefits:

  • Better Tree-Shaking: Your bundler can eliminate unused code more effectively
  • Optimal Bundling: Your build process optimizes the code for your specific use case
  • Smaller Bundle Size: No duplicate dependencies or unnecessary polyfills
  • Modern Output: Your bundler can target your specific browser requirements

Requirements for Consuming Apps

Your Next.js application needs to be configured to transpile the library:

// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  transpilePackages: ['@your-org/nextjs-library'],
};

module.exports = nextConfig;

Tailwind CSS Integration

To use the library's Tailwind styles in your Next.js project:

  1. Import the library's Tailwind config:
// tailwind.config.js
import libraryConfig from '@your-org/nextjs-library/tailwind';

export default {
  ...libraryConfig,
  content: [
    ...libraryConfig.content,
    './pages/**/*.{js,ts,jsx,tsx,mdx}',
    './components/**/*.{js,ts,jsx,tsx,mdx}',
    './app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
};
  1. Import the library styles:
// pages/_app.js or app/layout.js or globals.css
import '@your-org/nextjs-library/styles';

// Alternative method (if the above doesn't work):
import '@your-org/nextjs-library/src/assets/styles/globals.css';

Contributing

Getting Started

  1. Install asdf and set Node.js version:

    # Install asdf if you haven't already
    # Then install the required Node.js version
    asdf install nodejs 22.17.1
    asdf local nodejs 22.17.1
  2. Install dependencies:

    yarn install
  3. Run tests to ensure everything works:

    yarn test

Development Workflow

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Make your changes following our component templates above
  4. Add tests for your changes (src/test/YourComponent.test.tsx)
  5. Run the test suite (yarn test)
  6. Test with yalc in a real Next.js app
  7. Commit your changes (git commit -m 'Add some amazing feature')
  8. Push to the branch (git push origin feature/amazing-feature)
  9. Open a Pull Request

Code Style

  • Use TypeScript for all files
  • Follow the existing component patterns
  • Use cn() utility for className merging
  • Include proper TypeScript interfaces
  • Add tests for new components/functions
  • Document props and functions with JSDoc comments

License

MIT License - see the LICENSE file for details.

export default tseslint.config([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...

      // Remove tseslint.configs.recommended and replace with this
      ...tseslint.configs.recommendedTypeChecked,
      // Alternatively, use this for stricter rules
      ...tseslint.configs.strictTypeChecked,
      // Optionally, add this for stylistic rules
      ...tseslint.configs.stylisticTypeChecked,

      // Other configs...
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
]);

You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:

// eslint.config.js
import reactX from 'eslint-plugin-react-x';
import reactDom from 'eslint-plugin-react-dom';

export default tseslint.config([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...
      // Enable lint rules for React
      reactX.configs['recommended-typescript'],
      // Enable lint rules for React DOM
      reactDom.configs.recommended,
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
]);