This project is an opinionated collection of tools, apps, and libraries intended for supporting rapid React UI development. It was heavily inspired by GitHub's Primer design system.
Note: This library is no longer under active development. It was a fun project while it lasted, but I've taken it internal for use within Qualcomm.
Live build here: https://tsx-monorepo.com/
Development for this project is ongoing - not sure how much longer the repo is going to stay public.
Mobile support is coming soon - just need to spruce up the sidebar. The nav menu doesn't have links yet, and you'll be stuck on the homepage unless you navigate to urls directly.
Table of contents
- Consistent code formatting in every package via ESLint.
- First class TypeScript support.
- Hot reloading adjacent packages for rapid development.
- First class documentation support via MDX and react-live.
- Consistent styling by use of styled-components and styled-system with a strongly typed design system.
- Tree shaking for every React library.
- This is accomplished using webpack's side effects. Learn more.
- Working "go to definition" feature in IDEs for every package.
- Icons by react-icons.
- Unit Testing support via Jest and React Testing Library.
- E2E testing with Playwright + Jest.
- Ensure that you have Node and Yarn installed.
- I run Node v14+ and haven't tried any lesser versions. Your mileage may vary if you're not on v14+.
- Yarn 2.0+ currently has an issue with the
&
operator inpackage.json
scripts. Use Yarn v1.22.10 until this is resolved.
- WebStorm/IntelliJ
- This should work out of the box with either of these. If the
Go to Declaration or Usages
option is taking you to the dist/ folder of the target component, try using theGo to Implementation(s)
option instead.
- This should work out of the box with either of these. If the
- VSCode
- The
Go to Definition
option works out of the box for me. I'd highly recommend binding this toCtrl+Click
.
- The
- Unless otherwise noted, run all
yarn
commands from the root of this repository. This is particularly important foryarn install
. For more details on why this is necessary, read up on yarn workspaces and lerna.
- Standalone Front End applications are located within
packages/apps
. - Shared libraries and utilities are located within
packages/libs
. - Data fetching APIs and libraries are located within
packages/data
.- Note: it's perfectly fine for isolated apps to include their own data fetching apis within their respective package folders.
The NextJS application at packages/docs
is responsible for rendering MDX components. Shared components that contain a corresponding mdx file are aggregated by this package. For more information, check out the docs readme.
- run
yarn install
from the root. - run
yarn docs
from the root of this repository. Executesyarn run docs --parallel --stream
which does the following:- starts
@rb/monorepo-docs
in dev mode usingnext dev
- runs
@rb/react-style-system
and@rb/react-components
in watch mode (usingtsc
) which triggers a recompile on change (hot reloading). - There's additional configuration details in the
packages/docs
package that forces the site to hot-reload when MDX files are changed.
- starts
- Every package must contain at least the following:
- a
package.json
file. - either a
src
folder with code specific to the package's library or framework or anindex.js/index.ts
that exports the code or configuration of the package ( JS/TS packages only). - a
tsconfig.json
(TypeScript projects only).
- a
- Within a TypeScript React app, it's recommended that path aliasing be setup such that directories from the local package starts with
~
.- The base ESLint configuration is set up to split import groups based on external, internal, and sibling/parent/child. A 4th grouping is configured for apps-specific internal files. This is done for organizational purposes, as it's much easier to determine the origin of the imported code.
- Example for a package with
src/components
andsrc/pages
folder:
// src/components/simple-div/SimpleDiv.tsx
import React from "react"
export default function SimpleDiv(): JSX.Element {
return <div />
}
// src/pages/about.tsx
import React from "react"
import SimpleDiv from "~components/simple-div/SimpleDiv.tsx"
import {localUtilityFunc} from "./utils"
export default function AboutPage(): JSX.Element {
return <SimpleDiv onClick={localUtilityFunc} />
}
Separate your styles from your api logic. This makes testing significantly easier and keeps your component files small and self-contained.
- A
Data
component does data fetching and then renders its correspondingView
component. That’s it.
// src/components/example/Example.tsx
import React from "react"
import useDataExample from "./utils"
export default function Example(): JSX.Element {
// Problem: we've now coupled this component to some arbitrary
// API logic. This component may not render properly if the api
// isn't available during testing (common during CI).
const {data} = useSomeApiData()
return (
<div>
{/* apply styles, etc... */}
{data}
</div>
)
}
// src/components/example/ExampleView.tsx
import React from "react"
export interface ViewProps {
// never use `any` like this. It's just an example.
data: any
}
export default function ExampleView({data}: ViewProps): JSX.Element {
return (
<div>
{/* apply styles, etc... */}
{data}
</div>
)
}
// src/components/example/ExampleData.tsx
import React from "react"
import ExampleView from "./ExampleView"
import useDataExample from "./utils"
export default function ExampleData(): JSX.Element {
const {data} = useSomeApiData()
return <TableView data={data} />
}
Now we're more flexible. We've eliminated the API coupling by placing the styles in a separate component. However, this presents with a tradeoff.
Pros:
- More flexible and likely more accurate tests.
- Can reuse the
ExampleData
container to supply the same logic to another component.
Cons:
- Additional file to maintain (potentially offset by SoC).
- We now have to mock the API logic in order for the tests to pass.
Read more on the Data/View pattern here.
- This is a lightweight Agile technique. It involves a project structure where your code is organized by what it accomplishes (i.e. features), rather than lumping all modules of like types into separate blobs of components, routes, logic, actions, etc.
- There is a direct correlation between the problem space (the requirements) and the implementation (the code)
-
Reusable components that are specific to an application but not a feature should reside in
src/components
. -
Reusable components without a specific application should be placed in the
react-components
package. -
For a given featured name
<feature-name>
, place all related code insrc/features/<feature-name>
.
- Should not include code specific to project.
- They should not rely on project specific code, constants, router, etc.
See the following blog posts. This guy is pretty good. I used his monorepo as a starting point for this repository: