npm package: @openzeppelin/contracts-ui-builder-renderer
Description: A specialized library for rendering customizable transaction forms for blockchain applications.
Author: OpenZeppelin
Latest version: 0.9.0
Deprecated: This package has been renamed to @openzeppelin/ui-builder-renderer. Please migrate: https://www.npmjs.com/package/@openzeppelin/ui-builder-renderer

# Renderer Package

[![npm version](https://img.shields.io/npm/v/@openzeppelin/contracts-ui-builder-renderer.svg)](https://www.npmjs.com/package/@openzeppelin/contracts-ui-builder-renderer)
[![License](https://img.shields.io/npm/l/@openzeppelin/contracts-ui-builder-renderer.svg)](https://github.com/OpenZeppelin/contracts-ui-builder/blob/main/LICENSE)

A specialized library for rendering customizable transaction form and other components for blockchain applications. Part of the UI Builder ecosystem.

## Installation

```bash
# Using npm
npm install @openzeppelin/contracts-ui-builder-renderer @openzeppelin/contracts-ui-builder-types

# Using yarn
yarn add @openzeppelin/contracts-ui-builder-renderer @openzeppelin/contracts-ui-builder-types

# Using pnpm
pnpm add @openzeppelin/contracts-ui-builder-renderer @openzeppelin/contracts-ui-builder-types
```

## Features

- Lightweight app rendering components
- Framework-agnostic design
- TypeScript support with full type definitions (via @openzeppelin/contracts-ui-builder-types)
- Support for both ESM and CommonJS environments
- Customizable styling options
- Optimized for blockchain transaction data

## Type System

This package uses type definitions from the `@openzeppelin/contracts-ui-builder-types` package, which serves as the single source of truth for types used across the UI Builder ecosystem. These types include:

- Form field and component definitions
- Layout and validation schemas
- Adapter interfaces for blockchain interactions

When using this package, you should also install `@openzeppelin/contracts-ui-builder-types` to ensure proper type checking in your application.

```tsx
// Example of importing types
import { TransactionForm } from '@openzeppelin/contracts-ui-builder-renderer';
import type {
  ContractAdapter,
  FormValues,
  RenderFormSchema,
} from '@openzeppelin/contracts-ui-builder-types';
```

## Component Styling

This package renders forms using UI components and field components sourced from the `@openzeppelin/contracts-ui-builder-ui` package. **This `renderer` package itself does not ship with pre-compiled CSS or contain UI component implementations directly.**

Styling relies on the consuming application (like `packages/builder` or an exported project) to:

1.  **Include Tailwind CSS** in its build process.
2.  **Configure Tailwind** to scan the `@openzeppelin/contracts-ui-builder-ui` package's source files within `node_modules` (Tailwind v4 does this automatically for dependencies) and also its own source files.
3.  **Import the shared theme** from the `@openzeppelin/contracts-ui-builder-styles` package (e.g., importing `packages/styles/global.css`).

This ensures that the necessary utility classes used by the components from `@openzeppelin/contracts-ui-builder-ui` are generated by the consumer's build, using the consistent theme.

## Usage

```tsx
import { TransactionForm } from '@openzeppelin/contracts-ui-builder-renderer';
// UI and Field components are now typically imported directly from @openzeppelin/contracts-ui-builder-ui if needed outside the form
// import { Button, TextField } from '@openzeppelin/contracts-ui-builder-ui';
import type {
  ContractAdapter,
  EvmNetworkConfig,
  NetworkConfig,
  RenderFormSchema,
} from '@openzeppelin/contracts-ui-builder-types';

// Example form schema
const schema: RenderFormSchema = {
  id: 'example-form',
  title: 'Example Form',
  fields: [
    // Your form fields here
  ],
  layout: {
    columns: 1,
    spacing: 'normal',
    labelPosition: 'top',
  },
  validation: {
    mode: 'onChange',
    showErrors: 'inline',
  },
  submitButton: {
    text: 'Submit',
    loadingText: 'Submitting...',
  },
};

// Example network configuration (replace with actual config from adapter packages)
const networkConfig: EvmNetworkConfig = {
  id: 'example-evm-network',
  name: 'Example EVM Network',
  ecosystem: 'evm',
  network: 'ethereum',
  type: 'testnet',
  isTestnet: true,
  chainId: 11155111,
  rpcUrl: 'https://rpc.example.com',
  explorerUrl: 'https://explorer.example.com',
  nativeCurrency: { name: 'ETH', symbol: 'ETH', decimals: 18 },
  apiUrl: 'https://api.example.com',
};

// Simple adapter implementation for demonstration.
// Real applications use adapters like @openzeppelin/contracts-ui-builder-adapter-evm
// crucially, the adapter instance should be configured for the specific networkConfig
const adapter: ContractAdapter = {
  // Adapter methods should use the networkConfig passed during instantiation
  // (or assume it was passed during instantiation as shown in adapter READMEs)
  networkConfig: networkConfig, // Adapters hold their config internally
  ecosystem: 'evm', // Example ecosystem
  loadContract: async (source: string) => {
    // Implementation would use this.networkConfig
    throw new Error('Not implemented');
  },
  mapParameterTypeToFieldType: (type: string) => 'text',
  getCompatibleFieldTypes: (type: string) => ['text'],
  generateDefaultField: (param: any) => ({ id: 'f', name: 'p', label: 'L', type: 'text' }),
  formatTransactionData: (functionId, inputs) => inputs,
  isValidAddress: (address) => !!address && address.length > 0,
  getWritableFunctions: (schema: any) => [],
  getSupportedExecutionMethods: async () => [],
  validateExecutionConfig: async (config: any) => true,
  isViewFunction: (func: any) => false,
  queryViewFunction: async (addr: string, funcId: string, params: any[]) => {
    // Implementation would use this.networkConfig
    return null;
  },
  formatFunctionResult: (result: any) => String(result),
  supportsWalletConnection: () => false, // Indicate no support in this simple example
  getExplorerUrl: (address: string) => `${networkConfig.explorerUrl}/address/${address}`, // Example usage
  getExplorerTxUrl: (txHash: string) => `${networkConfig.explorerUrl}/tx/${txHash}`, // Example usage
  // Other methods omitted for brevity...
};

function App() {
  const handleSubmit = (data: FormData) => {
    console.log('Form submitted with data:', data);
    // Process transaction using the configured adapter and network
  };

  return (
    <TransactionForm
      schema={schema}
      adapter={adapter}
      networkConfig={networkConfig}
      onSubmit={handleSubmit}
    />
  );
}
```

## API Reference

### `<TransactionForm>`

The main component for rendering transaction forms. It internally uses `DynamicFormField` which in turn renders specific field components (like `TextField`, `AddressField`, etc.) and basic UI primitives (like `Button`, `Input`) sourced from the `@openzeppelin/contracts-ui-builder-ui` package.

#### Props

| Prop            | Type                       | Description                                                          |
| --------------- | -------------------------- | -------------------------------------------------------------------- |
| `schema`        | `RenderFormSchema`         | The schema definition for the form                                   |
| `adapter`       | `ContractAdapter`          | The blockchain adapter instance (must be configured for the network) |
| `networkConfig` | `NetworkConfig`            | The specific network configuration object for the target network     |
| `onSubmit`      | `(data: FormData) => void` | Callback function when form is submitted                             |
| `previewMode`   | `boolean`                  | (Optional) Renders form in preview mode                              |
| `initialValues` | `FormData`                 | (Optional) Initial values for form fields [TODO]                     |
| `disabled`      | `boolean`                  | (Optional) Disables all form fields [TODO]                           |
| `loading`       | `boolean`                  | (Optional) Shows loading state [TODO]                                |
| `theme`         | `ThemeOptions`             | (Optional) Custom theme options [TODO]                               |

### Utilities

This package provides core app rendering logic and utilities. For UI components (Buttons, Inputs, Cards, etc.) and Field components (AddressField, TextField, etc.), please refer to the `@openzeppelin/contracts-ui-builder-ui` package.

#### `logger`

A pre-configured singleton instance of the Logger utility for consistent application logging.

```typescript
import { logger } from '@openzeppelin/contracts-ui-builder-renderer';

logger.info('MyComponent', 'Component loaded');
```

#### `generateId`

A utility function to generate unique IDs for form elements or other components.

```typescript
import { generateId } from '@openzeppelin/contracts-ui-builder-renderer';

const uniqueFieldId = generateId('field_');
```

## Configuration System

The renderer package includes a configuration system that defines dependencies and other settings. This configuration is used when forms are exported to ensure proper dependencies are included in the generated project.

### Renderer Configuration File

Create a `config.ts` file in the renderer package:

```typescript
// packages/renderer/src/config.ts
import type { RendererConfig } from './types/RendererConfig';

/**
 * Configuration for the renderer package
 */
export const rendererConfig: RendererConfig = {
  /**
   * Dependencies for specific field types
   * Only dependencies for fields used in a form will be included in exports
   */
  fieldDependencies: {
    // Date field dependencies
    date: {
      runtimeDependencies: {
        'react-datepicker': '^4.14.0',
      },
      devDependencies: {
        '@types/react-datepicker': '^4.11.2',
      },
    },

    // Select field dependencies
    select: {
      runtimeDependencies: {
        'react-select': '^5.7.3',
      },
      devDependencies: {
        '@types/react-select': '^5.0.1',
      },
    },

    // Complex fields like file uploads
    file: {
      runtimeDependencies: {
        'react-dropzone': '^14.2.3',
      },
    },

    // Basic fields don't need additional dependencies
    text: { runtimeDependencies: {} },
    number: { runtimeDependencies: {} },
    checkbox: { runtimeDependencies: {} },
    radio: { runtimeDependencies: {} },
  },

  /**
   * Core dependencies required by renderer
   * These will be included in all exported projects
   */
  coreDependencies: {
    react: '^19.0.0',
    'react-dom': '^19.0.0',
    'react-hook-form': '^7.43.9',
    '@openzeppelin/contracts-ui-builder-renderer': '^1.0.0',
    '@openzeppelin/contracts-ui-builder-ui': '^1.0.0',
    '@openzeppelin/contracts-ui-builder-types': '^1.0.0',
    '@openzeppelin/contracts-ui-builder-utils': '^1.0.0',
  },
};
```

### Dependency Management

The RendererConfig is used by the export system to:

1. Include core dependencies required by all forms
2. Add field-specific dependencies based on the fields used in a form
3. Separate runtime from development dependencies

#### Field-specific Dependencies

When a user exports a form, the system analyzes the fields used in the form and only includes dependencies for those specific field types. For example, if a form doesn't use a date picker, the 'react-datepicker' dependency won't be included.

#### Versioning Strategy

The system applies a semantic versioning strategy to dependencies:

1. For renderer packages, it uses caret ranges (^) to allow minor and patch updates
2. This enables exported forms to receive updates without needing to re-export the entire form

### Configuration Discovery

The Package Management System automatically discovers the renderer configuration. The exported `rendererConfig` constant name is expected by the system.

## Package Structure

```text
renderer/
├── src/
│   ├── components/              # Core rendering components
│   ├── hooks/                   # Rendering-specific hooks
│   ├── utils/                   # Rendering utilities
│   ├── types/                   # Package-specific types
│   ├── stories/                 # Storybook stories
│   ├── test/                    # Package-specific tests
│   ├── config.ts                # Renderer configuration
│   └── index.ts                 # Main package exports
├── scripts/                     # Build and development scripts
├── package.json                 # Package configuration
├── tsconfig.json                # TypeScript configuration
├── tsup.config.ts               # Build configuration
├── vitest.config.ts             # Test configuration
└── README.md                    # This documentation
```

## Development

### Build System

This package uses `tsup` for building, following the monorepo's standardized build configuration. This ensures proper ES module output with correct import extensions.

```bash
# Install dependencies
pnpm install

# Build the package (generates both ESM and CJS outputs)
pnpm build

# Run tests
pnpm test
```

### Build Output

The build process, which uses `tsup` and `tsc`, creates the following outputs in the `dist/` directory:

- `index.js` - ES module with proper `.js` extensions in imports
- `index.cjs` - CommonJS build for compatibility
- `index.d.ts` - TypeScript declaration files for the entire package.

**Note:** No CSS is generated or included in the `dist` output.

### Release Process

> **Note**: Automatic publishing is currently disabled during early development. The workflow is configured but commented out until the package is ready for production release.

This package is published to the public npm registry as part of the monorepo's release process using Changesets.

The release workflow (when enabled):

1. Runs when code is pushed to the main branch or via manual trigger
2. Runs tests and builds the package
3. Uses changesets to determine the next version based on changeset files
4. Publishes to npm with appropriate tags
5. Creates a GitHub release with generated release notes

Manual releases can be triggered through the GitHub Actions interface with a version parameter when needed during development.

### Commit Guidelines

This project follows the [Conventional Commits](https://www.conventionalcommits.org/) standard to automate version management and changelog generation.

Commit format:

```
<type>(<scope>): <description>

[optional body]

[optional footer(s)]
```

Common types:

- `feat`: A new feature (triggers a minor version bump)
- `fix`: A bug fix (triggers a patch version bump)
- `docs`: Documentation changes
- `style`: Code style changes (formatting, semicolons, etc)
- `refactor`: Code changes that neither fix bugs nor add features
- `perf`: Performance improvements
- `test`: Adding or updating tests
- `chore`: Changes to the build process or auxiliary tools

Breaking changes should be indicated by adding `BREAKING CHANGE:` in the commit body, which will trigger a major version bump.

## License

[MIT](https://github.com/OpenZeppelin/contracts-ui-builder/blob/main/LICENSE) © OpenZeppelin