Skip to content

Instantly share code, notes, and snippets.

@Klerith
Created April 8, 2026 20:10
Show Gist options
  • Select an option

  • Save Klerith/8feb1f2db913f95019407de5917fcd29 to your computer and use it in GitHub Desktop.

Select an option

Save Klerith/8feb1f2db913f95019407de5917fcd29 to your computer and use it in GitHub Desktop.
Subir un paquete de componentes a NPM

Crear y subir paquetes de React a NPM

DevTalles · fernando-herrera.com
Guía completa de configuración y despliegue
Stack: React · TypeScript · CSS Modules · Vitest · Rollup


Contenido

  1. Crear el proyecto
  2. Instalar dependencias
  3. Configurar package.json
  4. Estructura de carpetas
  5. Declaraciones de tipos — css-modules.d.ts
  6. Crear componentes .tsx
  7. Punto de entrada — src/index.ts
  8. Configurar tsconfig.json
  9. Configurar rollup.config.mjs
  10. Configurar Vitest
  11. Primer test
  12. Crear .npmignore
  13. Build del paquete
  14. Prueba local con npm link
  15. Publicar en NPM
  16. Usar la librería en tu proyecto

01. Crear el proyecto

Crea la carpeta del proyecto e inicializa el package.json.

mkdir mi-libreria-ui
cd mi-libreria-ui
npm init -y

02. Instalar dependencias

Bundler y plugins base

npm install --save-dev rollup @rollup/plugin-node-resolve
npm install --save-dev @rollup/plugin-commonjs rollup-plugin-peer-deps-external

TypeScript

npm install --save-dev typescript @rollup/plugin-typescript tslib

CSS Modules

npm install --save-dev rollup-plugin-postcss

Imágenes y assets (jpg, png, svg, gif)

npm install --save-dev @rollup/plugin-url

Testing

npm install --save-dev vitest jsdom

React como devDependency

npm install --save-dev react react-dom @types/react @types/react-dom

03. Configurar package.json

{
  "name": "@tu-usuario/mi-libreria-ui",
  "version": "1.0.0",
  "description": "Mi librería de componentes React",
  "main":   "dist/index.cjs.js",
  "module": "dist/index.esm.js",
  "types":  "dist/types/index.d.ts",
  "files":  ["dist"],
  "scripts": {
    "test":       "vitest run",
    "test:watch": "vitest watch",
    "build":      "rollup -c rollup.config.mjs"
  },
  "peerDependencies": {
    "react":     ">=17.0.0",
    "react-dom": ">=17.0.0"
  }
}

⚠️ React debe ir en peerDependencies, NO en dependencies, para evitar instancias duplicadas.


04. Estructura de carpetas

mi-libreria-ui/
  src/
    components/
      Button/
        Button.tsx
        Button.module.css
        index.ts
      Card/
        Card.tsx
        Card.module.css
        index.ts
    tests/
      math.test.ts
    index.ts
    css-modules.d.ts
  package.json
  rollup.config.mjs
  vitest.config.ts
  tsconfig.json
  .npmignore

05. Declaraciones de tipos

src/css-modules.d.ts

Le indica a TypeScript cómo interpretar importaciones de CSS Modules e imágenes.

declare module '*.module.css' {
  const classes: { readonly [key: string]: string };
  export default classes;
}

declare module '*.jpg' {
  const src: string;
  export default src;
}

declare module '*.png' {
  const src: string;
  export default src;
}

declare module '*.svg' {
  const src: string;
  export default src;
}

06. Crear componentes tsx

src/components/Button/Button.tsx

import React from 'react';
import styles from './Button.module.css';

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

const Button: React.FC<ButtonProps> = ({
  label, onClick, variant = 'primary'
}) => {
  return (
    <button onClick={onClick} className={styles[variant]}>
      {label}
    </button>
  );
};

export default Button;

src/components/Button/index.ts

export { default } from './Button';

07. Punto de entrada

src/index.ts

Exporta todos los componentes que el usuario final podrá importar desde la librería.

export { default as Button } from './components/Button';
export { default as Card }   from './components/Card';

08. Configurar tsconfig.json

{
  "compilerOptions": {
    "target":                    "ES2017",
    "module":                    "ESNext",
    "lib":                       ["ES2017", "DOM"],
    "jsx":                       "react-jsx",
    "declaration":               true,
    "declarationDir":            "dist/types",
    "emitDeclarationOnly":       false,
    "strict":                    true,
    "rootDir":                   "./src",
    "moduleResolution":          "bundler",
    "allowSyntheticDefaultImports": true,
    "esModuleInterop":           true,
    "skipLibCheck":              true,
    "types":                     ["vitest/globals"],
    "outDir":                    "dist"
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"]
}

"types": ["vitest/globals"] permite usar describe, test y expect sin importarlos explícitamente en cada archivo de test.


09. Configurar rollup.config.mjs

import resolve    from '@rollup/plugin-node-resolve';
import commonjs   from '@rollup/plugin-commonjs';
import typescript from '@rollup/plugin-typescript';
import peerDeps   from 'rollup-plugin-peer-deps-external';
import postcss    from 'rollup-plugin-postcss';
import url        from '@rollup/plugin-url';

export default {
  input: 'src/index.ts',
  output: [
    { file: 'dist/index.cjs.js', format: 'cjs', sourcemap: true },
    { file: 'dist/index.esm.js', format: 'esm', sourcemap: true },
  ],
  plugins: [
    peerDeps(),                                                         // maneja dependencias externas
    url({ include: ['**/*.jpg', '**/*.png', '**/*.svg', '**/*.gif'] }), // maneja archivos de imagen
    postcss({ modules: true, inject: true }),                           // habilita CSS Modules
    resolve(),                                                          // resuelve rutas de archivos
    commonjs(),                                                         // convierte CommonJS a ES6
    typescript({
      tsconfig: './tsconfig.json',
      declaration: true,
      declarationDir: 'dist/types',
    }),
  ],
};

⚠️ El orden de los plugins importa: url y postcss deben ir antes de resolve() y typescript().


10. Configurar Vitest

vitest.config.ts

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    environment: 'jsdom', // simula el DOM para tests de componentes
    globals:     true,    // describe/test/expect sin imports explícitos
  },
});

11. Primer test

src/tests/math.test.ts

test('suma basica', () => {
  expect(1 + 1).toBe(2);
});

Comandos para correr los tests:

npm test              # ejecución única — ideal para CI/CD
npm run test:watch    # modo watch interactivo — ideal en desarrollo

12. Crear .npmignore

Evita subir archivos innecesarios al registro de NPM.

src/
node_modules/
rollup.config.mjs
vitest.config.ts
tsconfig.json
.env

13. Build del paquete

Genera la carpeta dist/ con los archivos listos para publicar.

npm run build

✅ Verifica que dist/ contenga index.cjs.js, index.esm.js y la carpeta types/.


14. Prueba local con npm link

Enlaza la librería a tu proyecto consumidor para probar cambios en tiempo real, sin necesidad de publicar en NPM.

En el proyecto de la librería:

npm link

En el proyecto consumidor:

npm link @tu-usuario/mi-libreria-ui

Cada build se refleja automáticamente — sin reinstalar:

npm run build

Para desenlazar — el orden importa

1. Primero en el proyecto consumidor:

npm unlink @tu-usuario/mi-libreria-ui

2. Luego en la librería:

npm unlink

⚠️ El nombre en npm link debe coincidir exactamente con el campo name del package.json.


15. Publicar en NPM

Crea una cuenta en npmjs.com si aún no tienes una, luego ejecuta:

npm login

# Primera publicación
npm publish --access public

# Versiones siguientes
npm version patch   # 1.0.0 → 1.0.1
npm version minor   # 1.0.0 → 1.1.0
npm version major   # 1.0.0 → 2.0.0
npm publish

16. Usar la librería en tu proyecto

Instala el paquete:

npm install @tu-usuario/mi-libreria-ui

Importa y usa tus componentes:

import { Button, Card } from '@tu-usuario/mi-libreria-ui';

function App() {
  return (
    <div>
      <Button label="Hola" onClick={() => alert('click')} />
      <Card title="Mi tarjeta" />
    </div>
  );
}

✅ Los tipos .d.ts se generan automáticamente — tendrás autocompletado completo en VSCode.


Stack completo

Herramienta Rol
rollup Bundler principal
rollup.config.mjs Configuración en ES Module nativo
@rollup/plugin-typescript Compila .ts / .tsx
rollup-plugin-postcss Procesa CSS Modules + inject: true
@rollup/plugin-url Incrusta .jpg .png .svg .gif
rollup-plugin-peer-deps-external Excluye React del bundle
css-modules.d.ts Tipado para CSS Modules e imágenes
tsconfig.json rootDir, moduleResolution: bundler, tipos de Vitest
Vitest + jsdom Testing con simulación de DOM

DevTalles · fernando-herrera.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment