This is a starter template for JavaScript libraries, using Rollup module bundler. It generates a dual-module package (ESM + CommonJS), ready to be published to NPM.
Read the accompanying blog post for full details on this template.
To use this template as the base of your project, clone it using git:
git clone https://github.com/mryechkin/rollup-library-starter.git <project-name>
cd <project-name>
Then make it your own:
rm -rf .git && git init && npm init
git add .
git commit -m "Initial commit"
Install dependencies:
npm i
Modify and structure code inside src
as needed.
Then, build the package:
npm run build
This will generate a dual-bundle package inside the dist
folder, with both esm
and cjs
versions of the library code (ES Modules, and CommonJS).
This template follows a file structure that generally looks like this:
src/
├── components/
│ ├── Component1/
│ │ ├── Component1.js
│ │ └── index.js
│ ├── Component2/
│ │ ├── Component2.js
│ │ └── index.js
│ └── index.js
├── utils/
│ └── index.js
└── index.js
Above, src
is the main folder for all of the library code. Inside it there are components
and utils
folders, and the main index.js
file.
The components
and utils
folders are there for example purposes, but you can use these as your starting point for organizing your library code.
The root-level index.js
will be the entry point into the library. Often called a "barrel" file, it exports everything that you'd like to have included in the bundle:
// src/index.js
export * from './components';
export * from './utils';
The above will export everything from components
and utils
, which in turn each have an index.js
of their own.
Having this root index.js
file is the only hard requirement for this template, as it's used by Rollup to configure the entry point of the library. When it comes to folders and how they're organized, that is entirely up to your library's needs.
Inside components
, there are two more folders - Component1
and Component2
- one for each individual component in our library. We also have an index.js
file to specify all the exports, similar to the root one:
// src/components/index.js
export { default as Component1 } from './Component1';
export { default as Component2 } from './Component2';
The above will re-export the default export of each specified folder (in this case Component1
and Component2
). If there were named exports, you can re-export those as well:
// src/components/index.js
export { default as Component1, foo, bar } from './Component1';
The convention for the component folders is to have a main component file Component1.js
, and an index.js
file with the exports.
For example, for the Button
component:
// src/components/Button/Button.js
const Button = (props) => {
// ...
};
export const VARIANT = {
PRIMARY: 'primary',
SECONDARY: 'secondary',
};
export default Button;
Here, we have the main Button
component itself as the default export, and the VARIANT
constant as a named export.
Then, inside the Button
index file, we re-export them:
// src/components/Button/index.js
import Button, { VARIANT } from './Button';
export default Object.assign(Button, {
VARIANT,
});
Notice the Object.assign()
above. This lets us assign the VARIANT
constant as a static property on the Button
component, allowing for something like this:
import { Button } from 'my-library';
<Button variant={Button.VARIANT.SECONDARY}>I'm a SECONDARY button</Button>;
This convention allows for a clean way to organize your modules, and works great with Rollup. Rollup will also warn you if there are any circular dependencies in your modules.
For more details, see the blog post.
This template includes the following Rollup plugins:
@rollup/plugin-alias
@rollup/plugin-babel
@rollup/plugin-commonjs
@rollup/plugin-node-resolve
@rollup/plugin-terser
rollup-plugin-analyzer
rollup-plugin-preserve-directives
And the following Babel plugins:
For details of all the configuration options used in this template, please see the accompanying blog post.
This template supports usage of the "use client"
directive for denoting Client vs Server Component, thanks to the rollup-plugin-preserve-directives
plugin.
To mark a component as a Client Component, add 'use client'
at the very top of the file:
// src/components/Button.js
'use client';
const Button = (props) => {
// ...
};
// ...
The rollup-plugin-preserve-directives
plugin, together with the preserveModules
output option, ensures that the 'use client'
directive does not get discarded during build time.
Warning It's important that
preserveModules
is set totrue
for this to work as expected.
Note as well that if using the Object.assign()
pattern for sub-components, the component MUST be a Client Component, otherwise you may get an error like "Cannot dot into a client module from a server component."