Connect your library
Integrating Your Own Components
This guide shows how to bring your own React components into UXPin Merge. By the end, your components appear in the UXPin Editor and stay in sync with your code. It’s written for developers who work with a React component library in Git.
Setting up the environment and rendering your first component shouldn’t take more than 30 minutes. A complete integration of a complex design system can take from 2 hours to 3–4 days.
Two ways to integrate
Clean integration. Merge works directly with your production code. No wrappers or code changes are necessary.
- Benefits: a simple workflow connecting designers and engineers, effortless maintenance (no extra work to update components in UXPin) and fast integration.
- Trade-offs: you need to follow Merge’s technical and code requirements, and you can’t modify component code for the benefit of designers using UXPin.
Wrapped integration. Merge works with wrappers for your components. Wrappers are typically HOC components.
- Benefits: a flexible integration with fewer problems meeting Merge’s requirements, and the ability to modify components to meet designers’ needs (for example, controlled checkboxes).
- Trade-offs: code maintenance is more complex and may need manual updates, and integration takes longer because you create a wrapper for every component.
Choose clean integration if your components already meet Merge’s requirements. Choose wrapped integration if you need to change how components behave for designers, or can’t change your source code.
Requirements
- Components: React components. React 16 to 18 are supported, and React 19 support is experimental. Web Components can be used through React wrappers.
- Node.js: 16 or later, to run the Merge CLI.
- Bundler: webpack 4 or 5 and webpack-cli 4 or 5, with a config that joins styles with components.
- Git: your library is in a Git repository.
- Directory structure: a single component per directory, and the filename defines the component name.
- Exports: component files have to contain
export defaultproviding the component. - Dialects: JavaScript (properties defined with PropTypes), Flow (properties defined with types) and TypeScript (properties defined with interfaces).
- Access to Merge: if you don’t have it yet, contact support at hello@uxpin.com.
- Browser: UXPin works best in the Chrome browser.
Clean integration, step by step
1. Install the Merge CLI in your project:
npm install @uxpin/merge-cli --save-dev2. Create the config files. Run npx uxpin-merge init. It creates uxpin.config.js, uxpin.webpack.config.js, a wrapper in src/components/UXPinWrapper/UXPinWrapper.js and an example Button in src/components/Button. It doesn’t overwrite files that already exist.
3. Add your components to uxpin.config.js. To make debugging easier, start with one component and add the rest one by one. A starting config looks like this:
module.exports = {
components: {
categories: [
{
name: 'General',
include: ['src/components/Button/Button.js'],
},
],
wrapper: 'src/components/UXPinWrapper/UXPinWrapper.js',
webpackConfig: 'uxpin.webpack.config.js',
},
name: 'My Design System',
};See Config File for all options.
4. Check your webpack config. Merge uses webpack to bundle JavaScript, CSS and the assets you import, such as icons and font files, and it merges your config with its own settings. If your config is complex, create a separate one for Merge. See Webpack Configuration.
5. If you need to provide a theme or other context for your components, create a wrapper component. It’s a HOC you can place in any directory in your repository. Here’s a wrapper with a theme provider:
import React from 'react';
import { ThemeProvider } from 'your-ui-library';
import theme from './theme';
export default function UXPinWrapper({ children }) {
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
}See Wrapper Component for more.
6. Add a preset, so Merge knows how to render the component first. Put preset files in a presets folder inside the component’s directory. A preset file name is a number, a dash and a name, for example 0-default.jsx. The number sets the order.
import React from 'react';
import Button from '../Button';
export default (
<Button uxpId="button1" label="Button" disabled={false} />
);The uxpId is a unique identifier. Two elements in one preset can’t have the same uxpId.
7. Preview your components. Run this from the root of your repository:
npx uxpin-mergeThis starts experimental mode, where you can see your library and components in the UXPin Editor.
8. Push your library to UXPin. First create a library in the UXPin Editor: select “Import React or Web Components from Git repo” and copy the authentication token. Don’t share it and don’t commit it to Git.
Then run:
npx uxpin-merge push --token <your-library-token>The wrapper and webpackConfig paths come from uxpin.config.js, so you don’t need to pass them as flags.
To send components automatically on every change, use a CI server. See Connecting code to UXPin (CI & push). A one-time push is an alternative, but only if you don’t plan frequent updates.
Wrapped integration, step by step
1. Decide where the wrappers live. You can create new directories for wrapper components inside your existing repository, for example ./src/components/Button/Merge/Button/Button.js, or create a new repository for them. Both work the same in this guide, but they change how you manage and release your components.
2. Install the Merge CLI as a dev dependency, as in step 1 above.
3. Create your first wrapper. A typical wrapper is a HOC React component that imports and returns your original component:
import React from 'react';
import PropTypes from 'prop-types';
import { Button as ButtonM } from '../../Button';
function Button(props) {
return <ButtonM {...props}>{props.children}</ButtonM>;
}
Button.propTypes = {
children: PropTypes.node,
size: PropTypes.oneOf(['small', 'large', 'medium']),
};
export default Button;Merge requires the parent directory and the exported component to have the same name. This name shows up in the UXPin Editor and in Spec mode, so the wrapper must be named like the original component. To avoid a name conflict, import the original under a different name, such as ButtonM.
4. Continue with steps 2 to 8 of the clean integration, with one difference: point include in uxpin.config.js at your wrapper files, for example src/components/Button/Merge/Button/Button.js.
Troubleshooting
- Some styles appear broken. Your styles may interfere with UXPin CSS, or the other way round. Scope your styles locally to avoid conflicts with UXPin CSS.
- My components are distributed as individual npm packages. Consider writing a wrapper (HOC) for every component and importing your system as a dependency. See this example. You can also use the AI-Assisted Merge Integration, which works with npm packages.
Next steps
Config File
The config file tells Merge which components to import into UXPin and describes your library. It can also hold the paths to your webpack config and, optionally, a wrapper component.
Where it lives
The config file is a JavaScript file named uxpin.config.js stored in the root directory of your project. To use another path, pass it with the --config option (see CLI Tool). The file exports a JavaScript object with module.exports.
To create a starting config, run npx uxpin-merge init.
./uxpin.config.jsA basic config
module.exports = {
components: {
categories: [
{
name: 'General',
include: ['src/components/Button/Button.js'],
},
],
wrapper: 'src/components/UXPinWrapper/UXPinWrapper.js',
webpackConfig: 'uxpin.webpack.config.js',
},
name: 'My Design System',
};The name of the library shows up in the UXPin Editor once you upload your library to UXPin.

Options
name: The name of your library, shown in the UXPin Editor. If you leave it out, the default name is “Library from Code”.components.categories: A list of categories. Each category has anameand anincludekey.categories[].name: The category name, shown in the component list in the UXPin Editor.categories[].include: A glob pattern, or a list of glob patterns, matching the component files in the category.components.wrapper: The path to a wrapper component. Optional. See Wrapper Component.components.webpackConfig: The path to your webpack config. See Webpack Configuration.components.pageHeadTags: HTML tags, as a string or a list of strings, that Merge adds to the<head>of the page. Use it, for example, to load fonts.settings: An object that Merge sends to UXPin with your library. See Library settings below.
Categories
The category names appear in the list of components in the UXPin Editor. If you plan to import many components, create several categories to make navigation in UXPin easier. To add a category, add another object with the keys name and include to the array:
categories: [
{
name: 'General',
include: [
'src/components/Button/Button.js',
'src/components/Link/Link.js',
],
},
{
name: 'Navigation',
include: [
'src/components/Tabs/Tabs.js',
'src/components/Tab/Tab.js',
],
},
],If a category should contain only one component, you can assign the path directly to include:
{
name: 'Category',
include: 'src/components/YourComponent/YourComponent.js',
}Glob patterns
include also accepts glob patterns:
module.exports = {
components: {
categories: [
{
name: 'General',
include: [
'src/library/Avatar/Avatar.js',
'src/library/Checkbox/!(__tests__)/*.js',
'src/library/Form/!(__tests__)/*.js',
],
},
{
name: 'Card',
include: 'src/library/Card/!(__tests__)/*.js',
},
{
name: 'Icons',
include: 'src/library/Icon/!(index).js',
},
],
wrapper: 'src/components/UXPinWrapper/UXPinWrapper.js',
webpackConfig: 'uxpin.webpack.config.js',
},
};Default config
If uxpin.config.js doesn’t exist or isn’t in the root directory, Merge prints a message and loads the default config:
module.exports = {
components: {
categories: [
{
name: 'Uncategorized',
include: [
'src/components/*/*.{js,jsx,ts,tsx}',
'src/*/*.{js,jsx,ts,tsx}',
],
},
],
},
name: 'Library from Code',
};Library settings
The settings object is sent to UXPin with your library. It turns on features for your library in UXPin. Two are documented:
- AI support in Forge and Wire:
settings.useAI. See Forge with React libraries. - Custom styles and custom props:
settings.useUXPinProps. See Using React.js Components.
module.exports = {
components: { /* ... */ },
name: 'My Design System',
settings: {
useAI: {
name: 'My Design System',
documentationUrl: '',
systemPrompt: '',
},
useUXPinProps: true,
},
};The settings option needs @uxpin/merge-cli 3.4.3 or newer.
Webpack Configuration
Merge uses webpack to bundle your component files into one JavaScript bundle that it sends to UXPin. This page explains how to set up your webpack config, so Merge can bundle your components together with their styles and assets.
Requirements
- webpack 4 or 5, and webpack-cli 4 or 5, installed in your project.
- The location of your webpack config stored in the Config File under
webpackConfig. You can also pass it with the--webpack-configoption. See CLI Tool.
How Merge uses your config
Merge combines your webpack config with its own settings. Its settings define the entry point, the output (a CommonJS library file named designsystemlibrary.js) and the build mode. If you don’t provide a config, Merge uses only its own settings.
Rules for your config
All the code and encoded files are merged into one bundle sent to UXPin. As a result, your webpack config has to be built in a way that doesn’t export any external files.
- For CSS, don’t use mini-css-extract-plugin. Use style-loader and css-loader to load your CSS into the JavaScript bundle.
- For imports of font files or images, use url-loader instead of file-loader.
If you use webpack 5, asset modules do the same job: a rule with type: 'asset/inline' inlines the imported file into the bundle as a data URI.
{
test: /\.(png|jpe?g|gif|svg|woff2?|ttf|eot)$/,
type: 'asset/inline',
},If this approach doesn’t fit your current webpack config, create a separate config for Merge.
A basic config
This is the config that npx uxpin-merge init creates in uxpin.webpack.config.js. It serves a repository with CSS loaded through style-loader and css-loader.
const path = require('path');
const webpack = require('webpack');
module.exports = {
output: {
path: path.resolve(__dirname, 'build'),
filename: 'bundle.js',
publicPath: '/',
},
resolve: {
modules: [__dirname, 'node_modules'],
extensions: ['*', '.js', '.jsx'],
},
devtool: 'eval-source-map',
module: {
rules: [
{
test: /\.(s*)css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
],
},
{
loader: require.resolve('babel-loader', { paths: ['./node_modules/@uxpin/merge-cli'] }),
test: /\.js?$/,
exclude: /node_modules/,
options: {
presets: [
require.resolve('@babel/preset-env', { paths: ['./node_modules/@uxpin/merge-cli'] }),
require.resolve('@babel/preset-react', { paths: ['./node_modules/@uxpin/merge-cli'] }),
],
},
},
],
},
};Babel comes with the Merge CLI, which is why the config resolves babel-loader, @babel/preset-env and @babel/preset-react from it. style-loader and css-loader are not part of the Merge CLI, so install them in your project.
More examples
Add these rules to the module.rules array of the basic config.
CSS-in-JS (Emotion) and SVG icons inlined into an Icon component:
{
test: /\.svg$/,
exclude: /node_modules/,
loader: 'svg-react-loader',
},Sass and inlined SVG icons:
{
test: /\.svg$/,
loader: 'svg-react-loader',
},
{
test: /\.(s*)css$/,
use: [
{ loader: 'style-loader' },
{ loader: 'css-loader', options: { importLoaders: 2 } },
{ loader: 'sass-loader' },
],
},Next steps
Wrapper Component
A wrapper component is a React component that wraps everything Merge renders in UXPin. Use it to provide a theme, import a shared style file, or give your components any other context they need. A wrapper is optional. It’s typically a HOC React component.
You can store a wrapper component anywhere in the repository, but you have to refer to its location in the Config File under wrapper. You can also pass it with the --wrapper option. See CLI Tool.
module.exports = {
components: {
categories: [
{
name: 'First Category',
include: [
'src/components/FirstComponent/FirstComponent.js',
'src/components/SecondComponent/SecondComponent.js',
],
},
],
wrapper: 'src/components/UXPinWrapper/UXPinWrapper.js',
webpackConfig: 'uxpin.webpack.config.js',
},
name: 'Name of your Library',
};npx uxpin-merge init creates a basic wrapper in src/components/UXPinWrapper/UXPinWrapper.js.
A basic wrapper
The most basic wrapper component is a React component that returns its children:
import React from 'react';
export default function UXPinWrapper({ children }) {
return children;
}Passing a theme provider
A wrapper can implement a theme provider. As a result, the theme styles are attached to every component rendered in UXPin:
import React from 'react';
import { ThemeProvider } from 'your-ui-library';
const theme = {
palette: {
primary: { main: '#006CFF' },
secondary: { main: '#444444' },
},
};
export default function UXPinWrapper({ children }) {
return <ThemeProvider theme={theme}>{children}</ThemeProvider>;
}Importing a common style file
A wrapper can also import a common style file. Webpack captures any import in the wrapper and adds it, after compilation, to the JavaScript package sent to UXPin:
import React from 'react';
import '../../styles/sass/common.scss';
export default function UXPinWrapper({ children }) {
return children;
}Loading fonts
For fonts stored in external services, such as Google Fonts or Adobe Fonts, add the tags to pageHeadTags in the config file. Merge adds them to the <head> of the page.
module.exports = {
components: {
categories: [ /* ... */ ],
pageHeadTags: [
'<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">',
'<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">',
],
},
name: 'Name of your Library',
};For font files in your repository, import them directly, so webpack handles them. See Webpack Configuration.
Next steps
Storybook integration
UXPin Merge Storybook integration
Design faster with Storybook components. Connect your own Storybook, public or private, to your UXPin account and use its interactive, code-based components in the UXPin Editor. You build prototypes with the same components your developers use, and you don’t need to know how to code.
Merge supports all Storybook frameworks.
Storybook integration is an alternative to the Git repository integration. Both let you design with pre-built code components. The main difference is how you add the components to UXPin.
Note
This page doesn’t show how to create a Storybook. It shows how to integrate an existing one with UXPin. Storybook is a development environment for UI components: it lets you browse components as a library, add interactions, view them in different states and test them in a safe environment. For details, see the Storybook website and its introduction guide.
Before you start
You need two things:
- Access to Merge in UXPin. If you don’t have it yet, contact support at hello@uxpin.com.
- Your Storybook URL. If your Storybook is open or public, you can go straight to the integration. If it’s private, you deploy it to UXPin with the Storybook deployer, as described below.
Note
If you use Vite with Storybook, add one more setting before you integrate. Vite assumes assets are in the root directory, so in your Storybook main.* file, add config.base = '' in viteFinal. See the Vite documentation for details.
Integrate a public Storybook
1. Open a new or existing prototype from the UXPin Dashboard.
2. Select the ‘Design System Libraries’ tab at the bottom left, then click ‘+ New Library’.
3. Select ‘Import Components from Storybook’, enter the URL of your Storybook and save the library.
Integrate a private Storybook
1. Follow the same steps as for a public Storybook, but select “Private” and copy the token.
2. Install the Storybook deployer in your design system:
npm install @uxpin/storybook-deployer --save-dev3. Build your Storybook as static files, then deploy it to UXPin:
npx uxpin-storybook-deployer -t TOKEN -s path/to/your/storybook- -s, --source <path>: the directory with your static Storybook build. Default: storybook-static.
- -t, --token <token>: the auth token for your library.
Note
With a private Storybook, your Storybook is protected by a UXPin login session. This also means you can’t see Storybook components in Preview without logging in to an account that has permission to the library.
You’re good to go. Start dragging and dropping the components.
Change component properties
With Storybook’s mechanism, Args, you can change properties, slots, styles, inputs and more dynamically in the UXPin Editor. There’s no need to alter a component in Storybook: you can do it while designing in UXPin. Learn more about Args.
Just make sure:
- You have Storybook 6 or newer.
- Your story is written the way Storybook defines it. See Storybook: using args.
Note
There’s a similar feature called “knobs”, which isn’t supported, because Storybook announced it would be deprecated in future versions. See how Controls replace knobs.
Update Storybook components in UXPin
After you integrate a public Storybook with UXPin, components update automatically whenever you change a component in your Storybook. The updated component looks and works exactly like in your Storybook, so don’t worry about components being a bit off after the sync.
For a private Storybook, run the deployer again after you change your components, because the deployer sends a static build.