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!

Vite (React)

Want easy mode?

If you prefer, you can clone the monorepo for Maestros (remember to star the repo while you're there!) and use the application found in apps/starter-vite. Remove any apps you don't want and you're good to go!

Getting started

Create an app with Vite's scaffolder in your apps folder:

Terminal
cd apps
pnpm create vite
Terminal
cd apps
pnpm create vite

We'll also add a turbo.json to ensure all of our pipelines are set for this app:

apps/starter-vite/turbo.json
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"dotEnv": [
".env.production.local",
".env.production",
".env.local",
".env"
]
},
"lint": {},
"dev": {
"cache": false,
"persistent": true,
"dotEnv": [
".env.production.local",
".env.production",
".env.local",
".env"
]
}
}
}
apps/starter-vite/turbo.json
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"dotEnv": [
".env.production.local",
".env.production",
".env.local",
".env"
]
},
"lint": {},
"dev": {
"cache": false,
"persistent": true,
"dotEnv": [
".env.production.local",
".env.production",
".env.local",
".env"
]
}
}
}

Adding your configurations

If you've followed the Prettier, ESLint, and TypeScript pages here in Maestros, you'll want to integrate the configuration from these packages into your Vite app. Let's get those incorporated in one by one.

Prettier

First, add the scripts to package.json:

apps/my-app/package.json
{
"scripts": {
"format": "prettier . --check --cache --cache-location='node_modules/.cache/prettiercache'",
"format:fix": "prettier . --write --cache --cache-location='node_modules/.cache/prettiercache' --log-level=warn"
},
"devDependencies": {
// Replace latest with most recent version
"prettier": "latest"
}
}
apps/my-app/package.json
{
"scripts": {
"format": "prettier . --check --cache --cache-location='node_modules/.cache/prettiercache'",
"format:fix": "prettier . --write --cache --cache-location='node_modules/.cache/prettiercache' --log-level=warn"
},
"devDependencies": {
// Replace latest with most recent version
"prettier": "latest"
}
}

Prettier will use the configuration found in the root of your monorepo - but it won't respect the .prettierignore found there. That's okay, though, because we can add one within our application's directory so we can make sure we don't spend time formatting files we don't need to.

apps/my-app/.prettierignore
.env*
node_modules
dist
apps/my-app/.prettierignore
.env*
node_modules
dist

TypeScript

First, install your TSConfig presets to your application and making a type checking script:

apps/my-app/package.json
{
"scripts": {
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/tsconfig": "workspace:*", // or "*" for npm and yarn
// Replace latest with most recent version
"typescript": "latest"
}
}
apps/my-app/package.json
{
"scripts": {
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"@repo/tsconfig": "workspace:*", // or "*" for npm and yarn
// Replace latest with most recent version
"typescript": "latest"
}
}

Creating your Vite app using the scaffolder will generate two TypeScript configuration files (as long as you picked TypeScript), one for your application and one for vite.config.ts. We want to use our root configurations so we have more standardization for our TypeScript across our repository.

If you haven't yet, abstract these generated files into your TypeScript configuration package:

And use them in your TypeScript Configurations in your application:

apps/starter-vite/tsconfig.json
{
"extends": "@repo/tsconfig/vite.json"
}
apps/starter-vite/tsconfig.json
{
"extends": "@repo/tsconfig/vite.json"
}
apps/starter-vite/tsconfig.node.json
{
"extends": "@repo/tsconfig/vite.node.json"
}
apps/starter-vite/tsconfig.node.json
{
"extends": "@repo/tsconfig/vite.node.json"
}

ESLint

Using our Vite preset we can quickly have ESLint set up exactly like the rest of our Vite apps.

Add ESLint and its scripts to your application's package.json:

apps/starter-vite/package.json
{
"scripts": {
"lint": "eslint . --cache --cache-location 'node_modules/.cache/.eslintcache' --max-warnings 0",
"lint:fix": "eslint . --fix --cache --cache-location 'node_modules/.cache/.eslintcache' --max-warnings 0"
},
"devDependencies": {
"@repo/lint": "workspace:*", // or "*" for npm and yarn
// Replace latest with most recent version
"eslint": "latest"
}
}
apps/starter-vite/package.json
{
"scripts": {
"lint": "eslint . --cache --cache-location 'node_modules/.cache/.eslintcache' --max-warnings 0",
"lint:fix": "eslint . --fix --cache --cache-location 'node_modules/.cache/.eslintcache' --max-warnings 0"
},
"devDependencies": {
"@repo/lint": "workspace:*", // or "*" for npm and yarn
// Replace latest with most recent version
"eslint": "latest"
}
}

Then, integrate your configuration into eslintrc.js:

apps/starter-vite/eslintrc.js
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: [require.resolve('@repo/lint/vite.js')],
parserOptions: {
project: true,
},
plugins: ['@typescript-eslint'],
};
apps/starter-vite/eslintrc.js
/** @type {import("eslint").Linter.Config} */
module.exports = {
root: true,
extends: [require.resolve('@repo/lint/vite.js')],
parserOptions: {
project: true,
},
plugins: ['@typescript-eslint'],
};

Note: If you're not using TypeScript, you can remove parserOptions and plugins.

Using your UI package

Vite will compile TypeScript on-the-fly with zero-configuration so it doesn't matter what packaging pattern we are using. Install it into your Vite app and start building!

With Tailwind

We like to let the application compile Tailwind classes for us to keep things simple. We've written more about this pattern and why we do it that way on the Tailwind page.