This is an alpha, sneak peek of Monorepo Maestros. For this iteration, I'm getting all of my thoughts down. In the future, we'll have better information architecture, graphics, and other awesomeness. Your feedback is welcome!
Multiple Entrypoint Package
Using the knowledge we gained in the Internal Packages and Just-In-Time Packages, we can easily create packages with multiple entrypoints.
There could be several reasons that you'd want to create your package with multiple entrypoints:
- Runtime Constraints: In some cases, a Node package that makes it into the browser bundle can crash your application since it won't have server-side only APIs available. By splitting the imports into two separate entrypoints, you can create server-friendly and browser-friendly entrypoints.
- Organization: There are often opportunities to bring clarity to your code by splitting your imports amongst multiple entrypoints. We'll give you an example by building a UI component library down the rest of this page.
To pick up where we left off at the end of the Internal Packages page, we had a transpiled package that we could share to the rest of our workspaces with one entrypoint.
To give this package another entrypoint, we'll only need to make a few small changes.
Let's add another file to our source code for a couple icons:
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
Next, we'll need to make tsup
aware of the new entry to your package.
packages/ui/tsup.config.js
export default defineConfig((options) => ({
entry: [
"src/index.tsx",
"src/icons.tsx"
packages/ui/tsup.config.js
export default defineConfig((options) => ({
entry: [
"src/index.tsx",
"src/icons.tsx"
Note: We're using JavaScript here but will still get great autocomplete. 😄
packages/ui/tsup.config.js
export default defineConfig((options) => ({
entry: [
"src/index.tsx",
"src/icons.tsx"
packages/ui/tsup.config.js
export default defineConfig((options) => ({
entry: [
"src/index.tsx",
"src/icons.tsx"
Note: We're using JavaScript here but will still get great autocomplete. 😄
Last, we'll add the entrypoint to our package.json
.
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/icons.js"
}
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/icons.js"
}
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/icons.js"
}
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./icons": {
"types": "./dist/icons.d.ts",
"import": "./dist/icons.js"
}
You'll now be able to import from the entrypoint using:
import { Circle } from '@repo/ui/icons';
import { Circle } from '@repo/ui/icons';
import { Circle } from '@repo/ui/icons';
import { Circle } from '@repo/ui/icons';
To pick up where we left off at the end of the Just-In-Time Package page, we can add another entrypoint in two quick steps.
Let's add another file to our source code for a couple icons:
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
packages/ui/src/icons.tsx
export const Circle = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" />
</svg>
);
};
export const Square = (props: SVGAttributes<SVGElement>) => {
return (
<svg {...props} viewBox="0 0 50 50">
<rect x="10" y="10" width="30" height="30" />
</svg>
);
};
Now, we'll create another entrypoint to our code so that other applications and packages can import it directly.
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": "./src/index.tsx",
"./icons": "./src/icons.tsx"
},
"typesVersions": {
"*": {
"*": ["./src/index.tsx"],
"icons": ["./src/icons.tsx"]
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": "./src/index.tsx",
"./icons": "./src/icons.tsx"
},
"typesVersions": {
"*": {
"*": ["./src/index.tsx"],
"icons": ["./src/icons.tsx"]
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": "./src/index.tsx",
"./icons": "./src/icons.tsx"
},
"typesVersions": {
"*": {
"*": ["./src/index.tsx"],
"icons": ["./src/icons.tsx"]
{
"name": "@repo/ui",
"version": "0.1.0",
"private": true,
"exports": {
".": "./src/index.tsx",
"./icons": "./src/icons.tsx"
},
"typesVersions": {
"*": {
"*": ["./src/index.tsx"],
"icons": ["./src/icons.tsx"]
The "exports"
field make the code available for compilation while the "typesVersions"
field makes the type definitions available.
You'll now be able to import from the entrypoint using:
import { Circle } from '@repo/ui/icons';
import { Circle } from '@repo/ui/icons';
import { Circle } from '@repo/ui/icons';
import { Circle } from '@repo/ui/icons';