npm package: @mezo-org/mezo-clay
Description: Mezo Clay is Mezo's UI component library.
Latest version: 0.2.0-dev.2

# Mezo Clay

Mezo Clay is Mezo's UI component library.

## Development

### Installation

This project uses [pnpm](https://pnpm.io/installation) for package management.

To install dependencies run:

```sh
pnpm install
```

### Run the development server

This project uses [Storybook](https://storybook.js.org/) to view, develop and test UI components in isolation.

```sh
pnpm run dev
```

Open [http://localhost:6006/](http://localhost:6006/) to view it in the browser.

### Linting

```shell
pnpm run format
```

This will lint both config files and ts/tsx/js/jsx files. See the `package.json` for specific linting commands.

#### Pre-commit

The project includes a [pre-commit](https://pre-commit.com/) hook to automate linting when you commit. Please refer to the [docs](https://pre-commit.com/#install) to install.

### Testing

Storybook automatically runs component tests for each story (if written as a [play function](https://storybook.js.org/docs/writing-stories/play-function)) and are visible in the UI under the "Component Tests" tab. We've set up the [test-addon](https://storybook.js.org/docs/writing-tests/test-addon) to use Vitest as the test runner. You can also run all tests in the terminal:

```shell
pnpm run test
```

### Creating new components

Make sure to export your new component from `src/components/index.ts`. Then either run the generate exports script to update the `package.json`:

```shell
pnpm run generate-exports
```

or manually add it to the `exports` section of `package.json` like:

```json
  "exports": {
    ...
    "./my-component": {
      "types": "./dist/components/my-component/index.d.ts",
      "default": "./dist/components/my-component/index.js"
    },
  }
```

These steps make sure the component is included in the Clay package and make it available as a subpath export.

### Build

#### Build storybook

```shell
pnpm run build
```

The storybook build will be generated as the /storybook-static directory.

#### Build the library

```shell
pnpm run build:lib
```

The library is output as the /dist directory.

### Publishing to NPM

Before you build and publish to NPM, you'll want to increment the version in `package.json`.

#### Build the library (same as above)

```shell
pnpm run build:lib
```

#### Login to NPM

```shell
pnpm login
```

#### Test the Built Package Locally

Use `pnpm pack` to build a `.tgz` file that you can link to from your test application.

#### Publish

```shell
pnpm publish --access public
```

__Note__: You can use the `--dry-run` flag on `publish` if you want to preview what will be included in the release package without actually publishing.

Check out your new release on the [npm page](https://www.npmjs.com/package/@mezo-org/mezo-clay).

## Using Clay

### Install

```shell
pnpm add @mezo-org/mezo-clay
```

Add peer dependencies:

```shell
pnpm add baseui@0.0.0-next-18f01efe0 "react@^18.3.1" "react-dom@^18.3.1" "styletron-engine-monolithic@^1.0.0"
  "styletron-react@^6.1.1"
```

### Adding Clay to your React app

Wrap the root of your app with the `ClayProvider` component, like so:

```js
import { StrictMode } from "react"
import { createRoot } from "react-dom/client"
import { ClayLightTheme, ClayProvider } from "@mezo-org/mezo-clay"

import "@mezo-org/mezo-clay/fonts.css" // Font styles

import App from "./App.tsx"

createRoot(document.getElementById('root')!).render(
    <StrictMode>
      <ClayProvider theme={ClayLightTheme}>
        <App />
      </ClayProvider>
    </StrictMode>
  ,
)
```

Import the `@mezo-org/mezo-clay/fonts.css` to ensure that the proper fonts are included.

The Clay package supports both barrel style and subpath imports:

```js
// Import components (barrel style - backward compatible)
import { Button, Input } from "@mezo-org/mezo-clay"
import { ClayProvider, ClayLightTheme } from "@mezo-org/mezo-clay"

// Or use subpath imports (better for tree-shaking)
import { Button } from "@mezo-org/mezo-clay/button"
import { Input } from "@mezo-org/mezo-clay/input"
```

### Adding Clay to Next.js

> **Note:** Clay currently supports the **Pages Router** only. Clay is built on Base UI, which uses Styletron (a runtime CSS-in-JS library). Styletron requires React Context and hooks to generate styles at runtime, making it incompatible with React Server Components used in the App Router.

#### 1. Setup SSR in `_document.tsx`

```tsx
import { styletronSheet } from "@mezo-org/mezo-clay/ssr"
import { DocumentContext, Head, Html, Main, NextScript } from "next/document"
import type { DocumentProps } from "next/document"

export default function Document(props: DocumentProps) {
  return (
    <Html>
      <Head>
        {/* Pass the styletron stylesheets to the <head> */}
        {props.__NEXT_DATA__.props?.styletronSheets}
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  )
}

Document.getInitialProps = async (ctx: DocumentContext) => {
  const engine = styletronSheet.getStyletronEngine()

  const page = await ctx.renderPage({
    enhanceApp: (App) => (props) => <App {...props} styletronEngine={engine} />
  })

  return {
    ...await ctx.defaultGetInitialProps(ctx),
    ...page,
    styletronSheets: styletronSheet.getStylesheets(engine)
  }
}
```

#### 2. Setup ClayProvider in `_app.tsx`

```tsx
import { ClayProvider, ClayLightTheme } from "@mezo-org/mezo-clay"
import { styletronSheet } from "@mezo-org/mezo-clay/ssr"
import type { AppProps } from "next/app"
import { useMemo } from "react"

import "@mezo-org/mezo-clay/fonts.css"

interface CustomAppProps extends AppProps {
  styletronEngine?: any
}

export default function App({
  Component,
  pageProps,
  styletronEngine
}: CustomAppProps) {
  const engine = useMemo(
    () => styletronEngine ?? styletronSheet.getStyletronEngine(),
    [styletronEngine]
  )

  return (
    <ClayProvider theme={ClayLightTheme} engine={engine}>
      <Component {...pageProps} />
    </ClayProvider>
  )
}
```

#### 3. Use Subpath Imports (Required for SSR)

```tsx
// ✅ Use subpath imports for SSR compatibility
import { Button } from "@mezo-org/mezo-clay/button"
import { Input } from "@mezo-org/mezo-clay/input"
import { HeadingLarge } from "@mezo-org/mezo-clay/typography"
import { useStyletron } from "@mezo-org/mezo-clay/styles"

// ❌ Avoid barrel imports in SSR contexts
import { Button, Input, HeadingLarge, useStyletron } from "@mezo-org/mezo-clay"
```

**Non-SSR-safe components** (Modal, Snackbar, Dropdown) must use `dynamic(() => import(...), { ssr: false })`.