Introduction
vite and tailwindcss are mainly used for application development, but can also be used for library development. Using vite offers the following advantages:
- Fast preview environment
- Automatic handling of CSS Modules
- Easy use of CSS preprocessors
- Apply path aliases
- Using vite as a Storybook bundler eliminates the need for an extra bundler
For Storybook, you can use the vite
bandler instead of webpack
.
For more information, see my previous post Using Vite for Bandler in Storybook.
The focus of library development is often on the test environment and documentation. In this article, we'll focus on the library itself, and show you how to create a fast build environment.
We will use the example of react as our code base. You may also find it useful to look at other frameworks supported by vite.
Building the environment
The first step is to generate a project skeleton.
We'll also create the following component as a suitable example component.
Create an entry point, or component, under src
.
The file structure looks like this:
Configuring path aliases
I haven't seen many projects that use path aliases in their libraries. However, it's often a good thing, as it means you don't have to modify the import path when refactoring, and it makes the import path easier to find.
I think one of the reasons for the low usage is that tsc
doesn't resolve path aliases by default when outputting type definition files.
The tool tsc-alias, which I'll introduce later, resolves path aliases in typedefs.
This solves the problem of path aliases, so first we need to set up a path alias.
The tsconfig.json
adds the following settings. This will allow VSCode to use IntelliSense for the import path.
Also, vite.config.ts
should look like this:
Configure tailwindcss for your library
The next step is to install tailwindcss.
Tailwindcss needs postcss
and should be set.
Also, import tailwindcss
in your entry file.
The example uses 3.0.0-alpha.1
, which is still an alpha release at the time of this writing, but 2.1
or higher with the JIT
engine is fine.
Now that tailwind.config.js
has been created, we can edit it.
In order to output CSS for the library, we need to make two changes
- Disable preflight to avoid outputting global scope CSS with side effects
- Set a prefix to adjust the class name output.
Preflight is the default style for tailwindcss, but it is inappropriate to use as a library because it affects the global scope. Check base.css for the default style generated by Preflight.
Also, if no prefix is used, the generated class name will be the same as the class name used by the application.
If the user of the library is using tailwincss and has customized the theme
field, it is possible to get an unintended style.
In order to deal with this, the tailwind.config.js
can be modified.
The tailwind class name now needs a prefix.
For example, this would look like:
CSS custom properties such as --tw-bg-opacity
should have no side effect, as their scope is closed.
Unfortunately, as a Bandler plugin, it should not be possible to use hash values for prefixes. 1
So be aware that even with a prefix, there is a chance of duplicate class names.
You can use the following CSS Modules without worrying about that.
CSS Modules
Vite supports CSS Modules by default.
The *.module.css
file is recognized as a CSS Modules. 2
Create a file called swipe.module.css
and add your styles.
To use this style, do the following Path aliases can also be used for CSS imports.
The output from the build will look something like this:
The output is a class name with a hashed suffix. In fact, if you only use CSS Modules, you don't need to set the tailwindcss prefix.
However, if you use both inline class notation and CSS Modules, it is safer to set the prefix.
CSS Modules and type declaration
In the case of TypeScript projects, the above import of CSS Modules results in a lint error. This is because there is no type definition for CSS Modules.
To solve this, you need to create a type definition file. To solve this, we need to create a type declaration file, which can be generated automatically by the CLI.
The tcm
command will be available.
You can run it in the format tcm <input directory>
. This will generate a CSS Modules type definition file.
This allows you to import class names in a type-safe manner.
You can also use the --watch
argument to monitor files. See typed-css-modules for more information.
CSS preprocessors
CSS preprocessors such as .scss
and .less
are also easily available.
Let's look at an example of using .scss
.
vite needs to be installed to handle the preprocessor. Also, the typed-css-modules
mentioned earlier do not support Sass by default.
There is a library called typed-scss-modules that can be used.
Let's change the stylesheet to .scss
.
The CLI interface is pretty same.
Now you can use Sass.
vite itself also supports .less
, and those type declaration can be output with typed-less-modules.
Build for libraries
Finally, let's check the build settings for libraries.
First, we need to clean up the external modules in package.json
Move react
from the dependencies
field to the peerDependencies
field.
Also, just writing it in peerDependencies
will not install it in node_modules
.
You should also add it to the devDependencies
field if you need it for development or build.
Next, change vite.config.ts
to look like this:
You can configure builds for libraries in the lib
field of the build
.
Also, in the case of the react library, the template for the vite project contains @vitejs/plugin-react.
This will generate bloated code in the form of jsx-runtime
by default.
As a library, we probably don't see much benefit in jsx-runtime
style output, so we'll change to classic style output.
Module format
By default, vite outputs ES Modules and UMDs.
UMDs require a global namespace; to output in UMD format, set the lib
name
field to an appropriate name.
In the example above, ES Modules and CommonJS are output.
Rename the output file
The default filename for the output is package.json
with name
+ module format + .js
.
In the example above, this would be mylib.es.js
and mylib.cjs.js
.
To change the file name of the output, set the fileName
of the lib
field.
This will create the files index.es.js
and index.cjs.js
under the dist
directory.
Disable dependency bundling
As a rule, libraries should not bundle dependencies. vite bundles all dependencies by default, so we'll disable this.
Specify the list of dependencies you want to exclude in the external
field of rollupOptions
.
You can do this by specifying peerDependencies
and dependencies
in package.json
.
Set the target environment.
You can specify which browser versions and Node.js runtime versions are supported.
The target
of build
can be chrome58
, node12
, etc. to generate code for that version.
By default, it targets browsers that natively support dynamic ES Moduls import.
Output source map
Include the source map in your build. The presence of a source map improves UX for library users, e.g. for debugging.
Set the build
sourcemap
field to true
.
With these settings, you can build by running vite build
.
The output should look something like this:
Output type definition files
We recommend using tsc
and tsc-alias
to output type definition files.
Path aliases are resolved by using tsc-alias
.
Change tsconfig.json
to look like this:
Don't forget to set the declarationMap
as well.
Output only type definition files with tsc
. Then overwrite the path alias with tsc-alias
.
This will result in the following output.
Run commands in parallel
Build and lint commands tend to be multiple. If there is no dependency between the order of each command, they can be run in parallel.
Use npm-run-all.
The shorthand CLI npm-run-all
and run-p
will be available.
An example of a parallel run command might look like this:
The build by vite and the type declaration output are independent, so they can be parallelized.
A run-s
command is also provided for sequential execution.
However, for short commands, &&
is sometimes more concise, as above.
Set the entry point
Finally, we need to set the entry point for package.json
.
The module
field should be set to the path of the ES Modules.
The exports
field should be set to the path of the .css
file, since we are including CSS files.
The sideEffects
field can be false
if your library does not contain any modules that affect global, such as polyfill.
Bundlers such as webpack
can make better use of tree-shaking.
If it does contain side-effects, see Mark the file as side-effect-free.
Now you're ready to publish. The only thing left to do is to publish to NPM.
To publish to NPM, please refer to Publish Typescript Packages with minimal configuration which I wrote before.
- maybe
windicss
can do it↩ - Also supports
.scss
and.less
. See the CSS preprocessor.↩
Edit this page on GitHub