← Назад к вопросам

Как вынести компоненты чтобы использовать их в другом проекте?

2.0 Middle🔥 182 комментариев
#React

Комментарии (2)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Извлечение и переиспользование компонентов в разных проектах

Это отличный вопрос о масштабировании и code reuse. Есть несколько подходов, от простых до продвинутых, в зависимости от масштаба и требований.

1. Монорепозиторий (Monorepo)

Для одной организации с несколькими проектами — это самый практичный подход:

projects/
├── web-app/
│   ├── src/
│   ├── package.json
│   └── tsconfig.json
├── mobile-app/
│   ├── src/
│   ├── package.json
│   └── tsconfig.json
└── packages/
    ├── ui-components/  <- Общие компоненты
    │   ├── src/
    │   │   ├── Button.tsx
    │   │   ├── Card.tsx
    │   │   └── index.ts
    │   ├── package.json
    │   └── tsconfig.json
    ├── shared-utils/   <- Утилиты
    ├── shared-hooks/   <- Кастомные хуки
    └── shared-types/   <- TypeScript типы

Инструменты для монорепо:

# Npm workspaces (встроено в npm 7+)
# package.json в корне
{
  "workspaces": [
    "packages/*",
    "projects/*"
  ]
}

# Yarn workspaces
yarn install  # Устанавливает всё одно раз

# pnpm
pnpm install

# Turborepo (для масштабирования)
turbo run build --filter=ui-components

# Nx (полнофункциональное решение)
nx generate @nrwl/react:library ui-components
nx build ui-components

Пример package.json в пакете:

{
  "name": "@myorg/ui-components",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/cjs/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "scripts": {
    "build": "tsc",
    "test": "vitest"
  }
}

Использование в другом проекте внутри монорепо:

// web-app/src/App.tsx
import { Button, Card } from '@myorg/ui-components';

export function App() {
  return (
    <Card>
      <Button variant="primary">Click me</Button>
    </Card>
  );
}

2. NPM пакет (отдельный репозиторий)

Если компоненты нужны в совсем разных проектах или они public:

# Создать npm пакет
mkdir ui-components
cd ui-components
npm init

Структура проекта-пакета:

ui-components/
├── src/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.test.tsx
│   │   └── index.ts
│   ├── Card/
│   │   ├── Card.tsx
│   │   ├── Card.test.tsx
│   │   └── index.ts
│   └── index.ts
├── dist/          <- Скомпилированный код
├── package.json
├── tsconfig.json
├── rollup.config.js  <- Конфиг для bundler
└── README.md

package.json для публикации:

{
  "name": "@mycompany/ui-components",
  "version": "1.0.0",
  "description": "Reusable React components",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist"
  ],
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/cjs/index.js",
      "types": "./dist/index.d.ts"
    },
    "./Button": {
      "import": "./dist/Button/index.js",
      "types": "./dist/Button/index.d.ts"
    }
  },
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "react": "^18.0.0",
    "typescript": "^5.0.0"
  },
  "scripts": {
    "build": "rollup -c",
    "test": "vitest",
    "prepublishOnly": "npm run build && npm run test"
  }
}

Rollup конфиг для сборки:

// rollup.config.js
import typescript from '@rollup/plugin-typescript';
import react from '@vitejs/plugin-react';

export default {
  input: 'src/index.ts',
  output: [
    {
      file: 'dist/index.js',
      format: 'es',
    },
    {
      file: 'dist/cjs/index.js',
      format: 'cjs',
    },
  ],
  external: ['react', 'react-dom'],
  plugins: [	ypescript(), react()],
};

3. GitHub Packages (приватные пакеты)

Для приватных компонентов в GitHub:

# .npmrc в проекте
echo "//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}" > ~/.npmrc
echo "@mycompany:registry=https://npm.pkg.github.com" >> ~/.npmrc

Публикация:

npm publish  # Публикуется в GitHub Packages

Установка в другом проекте:

npm install @mycompany/ui-components

4. Скриптовое извлечение (для legacy случаев)

Если нет возможности создать отдельный пакет:

# Copy-paste компонентов (самый простой способ)
cp src/Button/Button.tsx ../other-project/src/shared/

# Git subtree (более правильно)
git subtree add --prefix packages/ui shared-components main
git subtree pull --prefix packages/ui shared-components main

# Git submodule
git submodule add https://github.com/company/ui-components.git packages/ui

Лучшие практики для извлечения

1. Не зависит от конкретного проекта:

// Плохо — привязано к project-specific логике
const Button = () => {
  const store = useProjectStore(); // !! Зависимость от проекта
  return <button>{store.theme}</button>;
};

// Хорошо — принимает всё через props
interface ButtonProps {
  theme: 'light' | 'dark';
  onClick: () => void;
}

const Button = ({ theme, onClick }: ButtonProps) => (
  <button className={theme}>{label}</button>
);

2. Чистые зависимости:

// Плохо — много зависимостей
interface ComplexComponent {
  reduxStore: any;
  apiClient: any;
  analyticsService: any;
  authContext: any;
}

// Хорошо — минималистично
interface SimpleComponent {
  data: string;
  onSubmit: (value: string) => void;
}

3. TypeScript типы в отдельном файле:

// src/types.ts
export interface ButtonProps {
  variant: 'primary' | 'secondary';
  size: 'sm' | 'md' | 'lg';
  children: React.ReactNode;
  onClick?: () => void;
}

// src/Button.tsx
import type { ButtonProps } from './types';

export const Button = (props: ButtonProps) => { ... };

4. Документация и примеры:

# UI Components

## Button

```tsx

import { Button } from '@mycompany/ui-components';

<Button variant="primary" size="lg">
  Click me
</Button>

Props

  • variant: primary | secondary | danger
  • size: sm | md | lg
  • disabled: boolean

**5. Версионирование (Semantic Versioning):**

MAJOR.MINOR.PATCH 1.0.0 ^ ^ ^ | | └─ Patch: bug fixes (1.0.1) | └─── Minor: new features (1.1.0) └───── Major: breaking changes (2.0.0)


**6. Changelog:**

```markdown
# Changelog

## [1.1.0] - 2024-04-01
### Added
- New `disabled` prop to Button

### Changed
- Improved TypeScript types

## [1.0.0] - 2024-03-15
### Added
- Initial release with Button, Card components

Рекомендуемый workflow

# Для стартапа / одной компании
1. Монорепо (Npm workspaces) <- START HERE
   ├─ Просто
   ├─ Синхронизированные версии
   └─ Легко обновлять

# Для растущей организации
2. Монорепо + Turborepo
   ├─ Быстрые сборки
   ├─ Кэширование
   └─ Масштабирование

# Для коммерческих компонентов
3. Отдельный npm пакет
   ├─ Публичная документация
   ├─ Версионирование
   └─ Большая гибкость

# Для open source
4. Отдельный git репозиторий + npm registry
   ├─ Лицензирование
   ├─ Сообщество
   └─ CI/CD автоматизация

Итог

Для большинства случаев монорепо с npm workspaces — оптимальное решение:

  • Простая настройка
  • Синхронизированные версии
  • Легко обновлять компоненты
  • Масштабируется с Turborepo при росте

Для публичных/коммерческих пакетов — отдельный npm репозиторий с полноценным CI/CD.