React is a JavaScript toolkit that facilitates the creation of beautiful user interfaces for both online and mobile apps. It easily interfaces with other JavaScript frameworks and libraries and has short, reusable components.
Because of their great modularity, React component libraries speed up UI development and also provide a lot of freedom. There are several popular React UI libraries; however, they may not offer the level of customization needed for all projects.
In this tutorial, we’ll review how to build and publish a custom component library in React with Theme UI using TypeScript.
Theme UI is a library that uses constraint-based design concepts to create themeable user interfaces. With a broad API for best-in-class developer ergonomics, Theme UI can be used to create bespoke component libraries, design systems, web applications, Gatsby themes, and more.
This hands-on tutorial has the following prerequisites:
We’ll start by running the following command to create a folder for our project:
mkdir themecomponentui && cd themecomponentui
Next, we’ll initialize a new React project using the npm init
command to create a package.json
file. Then, we’ll install React and TypeScript with the following command:
npm i -D react @types/react typescript
The -D
flag in the above command denotes that the modules should be installed as devDependencies
because we’ll need them during our build process.
With React and TypeScript installed, let’s organize our project according to the below folder structure:
📦themecomponentui
┣ 📂src
┃ ┣ 📂components
┃ ┃ ┣ 📂Button
┃ ┃ ┣ 📂Input
Now, let’s configure TypeScript for our project.
We’ll use TypeScript to build the components that will enable us to use the library as a module.
First, we’ll configure TypeScript by creating a tsconfig.json
file using the following command:
npx tsc --init
Then, we’ll update the tsconfig.json
file with the following snippet:
{ "compilerOptions": { "esModuleInterop": true, "strict": true, "skipLibCheck": true, "jsx": "react", "module": "ESNext", "declaration": true, "declarationDir": "types", "sourceMap": true, "outDir": "dist", "moduleResolution": "node", "emitDeclarationOnly": true, "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, }, "exclude": [ "dist", "node_modules", ], }
In the above code, we added the following configurations:
"jsx": "react"
: Transforms JSX code into React code"skipLibCheck": true
: Skips type checking the declaration files"module": "ESNext"
: Generates Modern JavaScript modules for our library (ES6 or higher)"declarationDir": "types"
: Sets up a directory for the .d.ts files"sourceMap": true
: Enables mapping of JavaScript code back to its TypeScript file origins for debugging"outDir": "dist"
: Sets up the directory for project build"moduleResolution": "node"
: Follows Node.js rules for finding modules"emitDeclarationOnly": true
: Allows Rollup to generate JavaScript export type declarationsNow that TypeScript is configured, let’s move on to configuring Rollup.
Rollup is a JavaScript module bundler that combines tiny chunks of code to create something larger and more sophisticated, like a library or application. Instead of using unique solutions like CommonJS and AMD, it leverages the standardized ES module structure for code.
To get started with Rollup, we’ll install it using the following command:
npm i -D rollup
In the above code, we added the -D
flag to the Rollup installation command to add it to our devDependencies
. Once the installation is completed, we’ll also install the following Rollup plugins:
node_modules
.d.ts
filespeerDependencies
in a rollup bundleWe’ll run the following command to install the plugins:
npm i -D @rollup/plugin-node-resolve @rollup/plugin-commonjs @rollup/plugin-typescript rollup-plugin-peer-deps-external rollup-plugin-terser rollup-plugin-dts
When the installation is complete, we’ll configure Rollup by creating a rollup.config.js
file in the project root directory using the following snippets:
import resolve from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import typescript from "@rollup/plugin-typescript"; import dts from "rollup-plugin-dts"; import { terser } from "rollup-plugin-terser"; import peerDepsExternal from 'rollup-plugin-peer-deps-external'; const packageJson = require("./package.json"); export default [ { input: "src/index.ts", output: [ { file: packageJson.main, format: "cjs", sourcemap: true, }, { file: packageJson.module, format: "esm", sourcemap: true, }, ], plugins: [ peerDepsExternal(), resolve(), commonjs(), typescript({ tsconfig: "./tsconfig.json" }), terser(), ], external: ["react", "react-dom"] }, { input: "dist/esm/types/index.d.ts", output: [{ file: "dist/index.d.ts", format: "esm" }], plugins: [dts()], }, ];
In the above code, we configured CommonJS and ES modules to handle the project build process. This will enable us to use named export and tree shaking. It also provides enhanced static analysis, browser support, and compatibility with other JavaScript versions.
Next, we need to specify the path to the CommonJS files and ES modules in the package.json
file. We’ll open the package.json
file and add the following snippet configurations:
"main": "dist/cjs/index.js", "module": "dist/esm/index.js",
Finally, we’ll add the build command inside the scripts
object:
... "scripts": { ... "build": "rollup -c" }, ...
Now, let’s create our custom components. First, we’ll install Theme UI:
npm install -D theme-ui @emotion/react @emotion/styled @mdx-js/react
Theme UI is a flexible API framework, so we can choose to use a library of primitive UI components or use the sx
prop. For the demonstration purpose of this tutorial, we’ll use the sx
prop.
Next, let’s create the button component. We’ll create Button.tsx
and index.ts
files in the src/components/Button
folder.
Open the Button.tsx
file and add the following snippets:
/** @jsxImportSource theme-ui */ import * as React from "react"; import { MouseEventHandler } from "react"; export interface ButtonProps { label?: string; color?: string; fontFamily?: string onClick?: MouseEventHandler<HTMLButtonElement> } const Button = (props: ButtonProps) => { return <button onClick={props.onClick} sx={{ color: props.color, fontFamily: props.fontFamily }}>{props.label}</button>; }; export default Button;
In the above code, we added the /** @jsxImportSource theme-ui */
comment to the top of the file to enable the Theme UI sx
props.
Next, we imported the React framework MouseEventHandler
method to enable button click events.
Then, we defined the interface
ButtonProps
, specifying the Button
component properties. The ?
symbol denotes an optional property (e.g., color?: string
), meaning TypeScript will not throw an error if we do not provide that property in the component.
Next, we created the Button
component and set the styling using Theme UI’s sx
props with other properties.
Now, let’s export the Button
component in the index.ts
file using the following snippet:
export { default } from './Button'
Input
componentWe’ll create the Input
component by creating Input.tsx
and index.ts
files in the src/components/Input
folder.
Open the Input.tsx
file, and add the following snippet:
/** @jsxImportSource theme-ui */ import * as React from "react"; import { ChangeEventHandler } from "react" export interface InputProps { label?: string; disabled?: boolean; fontFamily?: string; placeholder?: string; paddding?: string; id?: string; onChange?: ChangeEventHandler<HTMLInputElement> } const Input = (props: InputProps) => { return ( <div> <label htmlFor={props.id ? props.id : 'text'}>{props.label}</label> <input type='text' id={props.id ? props.id : 'text'} disabled={props.disabled} placeholder={props.placeholder} sx={{ padding: !props.paddding && "4px", display: 'block' }}></input>; </div> ); }; export default Input;
In the above code, we added the /** @jsxImportSource theme-ui */
comment at the start of the file to use the Theme UI’s sx
prop to style the component.
Next, we defined the Input
component’s interface
. The onChange?
property will enable us to add an onChange
event to the input.
We created the Input
component, which takes in props
that match the InputProps
interface, and styled the component using Theme UI’s sx
prop and other properties.
Now, let’s export the Input
component in the index.ts
file using the following command:
export { default } from './Input'
We’ll create an index.ts
file in the src/components
folder, and export the components like so:
export { default as Button } from './Button'; export { default as Input } from './Input'
Now, we’ll build the project:
npm run build
The above command will create a dist
folder in the project root directory.
The next step is to publish our newly created components to npm so that we can use them in our project and share them with friends.
To get started, log into your npm account:
npm login
If you don’t have an account, you can sign up for an npm account here.
After logging in, enter your account credentials to get authenticated. An OTP code will be sent to your registered email address. Enter the OTP pin when requested.
Now, publish your package by running the following command:
npm publish --access public
You’ve successfully published a React custom component library to npm!
In this tutorial, we introduced the Theme UI library and demonstrated how to use Theme UI to build a custom React component library. We also demonstrated how to configure TypeScript and Rollup for the project build and how to publish a custom library to npm. You can extend this tutorial by creating more components with Theme UI, such as forms, boxes, and grids.
Thanks for reading. Please feel free to share and comment below.
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to implement one-way and two-way data binding in Vue.js, using v-model and advanced techniques like defineModel for better apps.
Compare Prisma and Drizzle ORMs to learn their differences, strengths, and weaknesses for data access and migrations.
It’s easy for devs to default to JavaScript to fix every problem. Let’s use the RoLP to find simpler alternatives with HTML and CSS.
Learn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
One Reply to "How to build a React custom component library with Theme UI"
here is my practical repository based on this tutorial
https://github.com/Haleem85/hideappbar.git