When building complex apps with multiple frontends, it can be useful to use a monorepo setup so you can import the same components into many different apps.

For this guide we’ll assume that you’re using Turborepo as your monorepo and Next.js for your frontend. Monorepo-specific functionality should work with any package manager or framework that supports workspaces. For more information visit the manual installation guide and the framework guide for your frontend of choice.

Create a package for Subframe

Since we’re using Turborepo, we can use the turbo generate command to create a package in our monorepo where we’ll be importing our Subframe components. We’ll call the package @repo/subframe.

If you’re using a different monorepo framework or just plain workspaces, you can just duplicate an existing package directory and change the contents accordingly.

npx turbo generate workspace --name @repo/subframe --type package

On the interactive prompt, make sure to select your monorepo’s TypeScript and ESLint config packages as devDependencies for the next step.

We’ll also want to integrate our monorepo’s TypeScript and ESLint configuration, so we’ll create the following two files:

import { config } from "@repo/eslint-config/react-internal";

/** @type {import("eslint").Linter.Config} */
export default config;

Install Subframe

We now want to install and configure Subframe in our new package. Let’s do so by running the @subframe/cli init command.

cd packages/subframe && npx @subframe/cli@latest init --sync --dir ui

Make sure that you set the import alias to @repo/subframe/* during the initialization process. This will make sure that the code imports from Subframe will work within our UI apps later. If you forgot to set it, simply edit the importAlias key in your .subframe/sync.json file and run @subframe/cli init again to sync the changed import alias to your Subframe project.

packages/subframe/.subframe/sync.json
{
  "directory": "ui",
  "importAlias": "@repo/subframe/*"
}

Export Subframe components from your package

To be able to import our components from this package in other apps in the monorepo, we’ll have to make sure they can find the components within our package, as well as Subframe’s TailwindCSS config. We can easily achieve this by setting the exports field in our package.json accordingly:

packages/subframe/package.json
{
  "name": "@repo/subframe",
  "version": "0.0.0",
  "private": true,
  "exports": {
    ".": "./ui/index.ts",
    "./components/*": "./ui/components/*.tsx",
    "./layouts/*": "./ui/layouts/*.tsx",
    "./tailwind-config": "./ui/tailwind.config.js"
  },
  "dependencies": {
    "@subframe/core": "^1.141.0"
  },
  "devDependencies": {
    "@repo/eslint-config": "*",
    "@repo/typescript-config": "*"
  }
}

Install the @repo/subframe package into your app

To install your local package to your app, add the following line to your frontend apps’ dependencies:

"@repo/subframe": "*"

Then run your package manager’s install command to link your dependencies:

npm install

Set up TailwindCSS in your frontend app

We assume you’ve already set up Tailwind CSS for your frontend app. If you haven’t done so, follow the Tailwind CSS guide.

The key change here is that we need to import Subframe’s tailwind config preset from our local package, as well as include the Subframe component source files in tailwind’s content array:

apps/web/tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
    "./app/**/*.{js,ts,jsx,tsx}",
    "./ui/**/*.{js,ts,jsx,tsx}",
    "../../packages/subframe/ui/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
  presets: [require("@repo/subframe/tailwind-config")],
};

Use Subframe components in your app

You can now import Subframe components directly into your app. Since we changed the import alias to the name of your local package, all imports generated by the Subframe app will resolve to the components in your @repo/subframe package.