PackShip v2 is now available! 🚀 Learn more

Frontend Setup

Setting up the frontend configuration for your PackShip project is essential for creating a well-structured and maintainable package. This guide will walk you through the basic setup process.

Project Structure

A typical PackShip frontend project has the following structure:

my-package/
├── src/
│   ├── index.tsx        # Main entry point
│   ├── components/      # React components
│   │   ├── Button/      # Component directory
│   │   │   ├── index.tsx
│   │   │   └── styles.css
│   │   └── Card/        # Another component
│   │       ├── index.tsx
│   │       └── styles.css
│   ├── utils/           # Utility functions
│   ├── hooks/           # Custom React hooks
│   ├── types/           # TypeScript type definitions
│   └── internal/        # (Optional) Internal components
├── dist/                # Compiled output
│   ├── cjs/             # CommonJS format
│   ├── esm/             # ES modules format
│   └── types/           # TypeScript declarations
├── node_modules/        # Dependencies
├── package.json         # Package configuration with _packshipInitialized flag
├── tsconfig.json        # TypeScript configuration
├── webpack.config.js    # Webpack configuration
├── babel.config.json    # Babel configuration
├── .eslintrc.json       # ESLint configuration
├── .gitignore           # Git ignore file
├── .npmignore           # npm ignore file
├── README.md            # Documentation
└── LICENSE.md           # License information

Entry Point

The entry point of your package is the src/index.tsx file. This is where you export all the components and utilities that should be available to consumers of your package.

// src/index.tsx
import Button from './components/Button';
import Card from './components/Card';
import { useToggle } from './hooks/useToggle';
import { formatDate } from './utils/formatters';
import type { ButtonProps, CardProps } from './types';

// Export components
export { Button, Card };

// Export hooks
export { useToggle };

// Export utilities
export { formatDate };

// Export types
export type { ButtonProps, CardProps };

// Default export (optional)
export default { Button, Card };

Only export components and utilities that you want to be part of your public API. Internal components and utilities in the src/internal directory should not be exported from the entry point as they are meant for internal use only.

Component Structure

Components should be organized in the src/components directory. Each component should have its own directory containing all related files.

Component Directory Structure

For a well-organized component, use a directory structure like this:

src/components/Button/
├── index.tsx         # Main component file
├── Button.test.tsx   # Component tests
├── styles.css        # Component styles
└── types.ts          # Component type definitions (optional)

Component Example

// src/components/Button/index.tsx
import React from 'react';
import './styles.css';

export interface ButtonProps {
  text: string;
  onClick?: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
}

const Button: React.FC<ButtonProps> = ({ 
  text, 
  onClick, 
  variant = 'primary',
  disabled = false
}) => {
  const baseClasses = "button";
  const variantClasses = `button--${variant}`;
  const stateClasses = disabled ? "button--disabled" : "";

  return (
    <button 
      className={`${baseClasses} ${variantClasses} ${stateClasses}`}
      onClick={onClick}
      disabled={disabled}
    >
      {text}
    </button>
  );
};

export default Button;

Component Styles

/* src/components/Button/styles.css */
.button {
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
  font-weight: 500;
  border: none;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.button--primary {
  background-color: #3b82f6;
  color: white;
}

.button--primary:hover:not(.button--disabled) {
  background-color: #2563eb;
}

.button--secondary {
  background-color: #e5e7eb;
  color: #1f2937;
}

.button--secondary:hover:not(.button--disabled) {
  background-color: #d1d5db;
}

.button--disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

Component Export

Each component should be exported from its directory using an index file. This makes imports cleaner for consumers of your package.

Styling Components

PackShip supports various styling approaches for your components:

1. Standard CSS

The simplest approach is to use standard CSS files imported directly into your components, as shown in the Button component example above.

2. CSS Modules

CSS Modules allow you to write CSS that is scoped to a specific component, preventing style conflicts.

// src/components/Card/styles.module.css
.card {
  border-radius: 0.5rem;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  padding: 1.5rem;
  background-color: white;
}

.cardHeader {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 1rem;
}

.cardBody {
  color: #4b5563;
}
// src/components/Card/index.tsx
import React from 'react';
import styles from './styles.module.css';

export interface CardProps {
  title: string;
  children: React.ReactNode;
}

const Card: React.FC<CardProps> = ({ title, children }) => {
  return (
    <div className={styles.card}>
      <div className={styles.cardHeader}>{title}</div>
      <div className={styles.cardBody}>{children}</div>
    </div>
  );
};

export default Card;

3. Styled Components

For CSS-in-JS styling, PackShip supports styled-components:

// Button.tsx
import React from 'react';
import styled from 'styled-components';

export interface ButtonProps {
  text: string;
  onClick?: () => void;
  variant?: 'primary' | 'secondary';
}

const StyledButton = styled.button<{ variant: 'primary' | 'secondary' }>`
  padding: 0.5rem 1rem;
  border-radius: 0.25rem;
  font-weight: 500;
  background-color: ${props => props.variant === 'primary' ? '#3b82f6' : '#e5e7eb'};
  color: ${props => props.variant === 'primary' ? 'white' : '#1f2937'};

  &:hover {
    background-color: ${props => props.variant === 'primary' ? '#2563eb' : '#d1d5db'};
  }
`;

const Button: React.FC<ButtonProps> = ({ 
  text, 
  onClick, 
  variant = 'primary' 
}) => {
  return (
    <StyledButton 
      variant={variant}
      onClick={onClick}
    >
      {text}
    </StyledButton>
  );
};

export default Button;

TypeScript Configuration

PackShip initializes your project with a pre-configured tsconfig.json file optimized for package development. Here's the default configuration for a React TypeScript project:

{
  "compilerOptions": {
    "target": "es2018",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": false,
    "jsx": "react-jsx",
    "declaration": true,
    "declarationDir": "./dist/types",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.test.tsx"]
}

Key TypeScript Configuration Options

  • target: Specifies the ECMAScript target version (es2018 provides good browser compatibility while supporting modern features).
  • jsx: Set to "react-jsx" for React 17+ projects, which doesn't require importing React in every file.
  • declaration: Generates .d.ts files to provide type information to consumers of your package.
  • declarationDir: Specifies where to output the type declaration files.
  • sourceMap: Generates source map files for easier debugging.
  • paths: Configures path aliases for cleaner imports (e.g., @/components instead of ../../components).

The TypeScript configuration is optimized for both development experience and package consumption. The declaration files are essential for providing type information to users of your package.