# Structure of Frappe UI Components in the ERPNext Banking Module: A Complete Guide

> Explore the Frappe UI component structure in ERPNext's banking module. Understand the React architecture, atomic UI, and routing for efficient development.

- Repository: [Frappe/erpnext](https://github.com/frappe/erpnext)
- Tags: architecture
- Published: 2026-05-20

---

**The ERPNext banking module implements a three-tier React architecture with atomic UI primitives in `components/ui`, domain-specific features in `components/features`, and route containers in `pages`, all built on Radix UI and Tailwind CSS.**

The banking module in ERPNext is a modern React application that follows a clean, layered component architecture. Understanding the **structure of Frappe UI components in the banking module** is essential for developers extending the reconciliation interface, statement importer, or settings panels. This guide breaks down the codebase into three distinct layers—primitives, features, and pages—based on the actual source implementation in the `develop` branch.

## The Three-Layer Component Architecture

The UI is organized into a strict hierarchy where each layer only knows about the one directly beneath it. This separation keeps business logic decoupled from styling concerns and allows for consistent theming across the application.

### Layer 1: Atomic UI Primitives (`src/components/ui/*`)

The foundation of the banking UI lives under `banking/src/components/ui`. These are **low-level, reusable components** that wrap Radix UI primitives with Tailwind CSS styling and accessibility logic.

**Core primitives include:**

- **`Button`** ([`button.tsx`](https://github.com/frappe/erpnext/blob/main/button.tsx)) – Implements `buttonVariants` using `class-variance-authority` (cva) to support `variant`, `size`, `theme`, and `isIconButton` props.
- **`Dialog`** ([`dialog.tsx`](https://github.com/frappe/erpnext/blob/main/dialog.tsx)) – Wraps Radix UI's `Dialog` primitive with overlay management and a `min-w-5xl` content container.
- **`Tooltip`** ([`tooltip.tsx`](https://github.com/frappe/erpnext/blob/main/tooltip.tsx)) – Provides `Tooltip`, `TooltipTrigger`, and `TooltipContent` components via Radix primitives.
- **`Tabs`** ([`tabs.tsx`](https://github.com/frappe/erpnext/blob/main/tabs.tsx)) – Vertical tab navigation built on `TabsPrimitive` for settings panels.
- **`Select`, `Input`, `Switch`, `Card`** – Additional form and layout primitives following the same pattern.

All primitives share three common traits:

1. **Styling** – Tailwind CSS utility classes composed with the `cn` helper from [`src/lib/utils.ts`](https://github.com/frappe/erpnext/blob/main/src/lib/utils.ts).
2. **Variants** – `cva` enables prop-based styling without duplicate CSS.
3. **Accessibility** – Components forward `ref` and `aria-*` attributes from underlying Radix components.

These primitives are framework-agnostic and can be used anywhere within the banking UI without introducing business logic.

### Layer 2: Feature Components (`src/components/features/*`)

Feature components assemble primitives into **domain-specific screens** for banking workflows. These files import UI primitives directly and add API calls, state management, and banking-specific layouts.

**Major feature areas:**

- **Settings** ([`Settings.tsx`](https://github.com/frappe/erpnext/blob/main/Settings.tsx), [`SettingsDialog.tsx`](https://github.com/frappe/erpnext/blob/main/SettingsDialog.tsx), [`Preferences.tsx`](https://github.com/frappe/erpnext/blob/main/Preferences.tsx), [`MatchingRules.tsx`](https://github.com/frappe/erpnext/blob/main/MatchingRules.tsx)) – A dialog with vertical tab navigation using `SettingsTabs` and `SettingsPanel`. Imports `Button` with `variant="outline"` and the dialog primitives.
- **Bank Statement Importer** ([`BankStatementImporter.tsx`](https://github.com/frappe/erpnext/blob/main/BankStatementImporter.tsx), [`CSVImport.tsx`](https://github.com/frappe/erpnext/blob/main/CSVImport.tsx), [`StatementDetails.tsx`](https://github.com/frappe/erpnext/blob/main/StatementDetails.tsx)) – A multi-step wizard for CSV upload, column mapping, and statement import. Uses `FileDropzone`, `Input`, and custom hooks like `usePaymentEntryCalculations`.
- **Bank Reconciliation** ([`BankReconciliationStatement.tsx`](https://github.com/frappe/erpnext/blob/main/BankReconciliationStatement.tsx), [`BankTransactionList.tsx`](https://github.com/frappe/erpnext/blob/main/BankTransactionList.tsx), [`MatchFilters.tsx`](https://github.com/frappe/erpnext/blob/main/MatchFilters.tsx)) – Transaction lists, filtering UI, and reconciliation modals leveraging `Table`, `Dialog`, `Switch`, and `Badge`.
- **Action Log** ([`ActionLog.tsx`](https://github.com/frappe/erpnext/blob/main/ActionLog.tsx)) – Timeline display for bank entry audit trails.

Feature components import primitives using path aliases:

```typescript
import { Button } from '@/components/ui/button';
import { SettingsDialog, SettingsTabs, SettingsTabItem } from '@/components/ui/settings-dialog';

```

### Layer 3: Page Containers (`src/pages/*`)

Page containers serve as **entry points for the router**, composing feature components into full pages. They handle routing logic and global provider injection but contain minimal UI logic themselves.

**Key pages:**

- **[`BankReconciliation.tsx`](https://github.com/frappe/erpnext/blob/main/BankReconciliation.tsx)** – Renders the `BankReconciliation` feature component as the default application route.
- **[`BankStatementImporterContainer.tsx`](https://github.com/frappe/erpnext/blob/main/BankStatementImporterContainer.tsx)** – Sets up nested routing (`/statement-importer/*`) and wraps the importer UI.
- **[`ViewBankStatementImportLog.tsx`](https://github.com/frappe/erpnext/blob/main/ViewBankStatementImportLog.tsx)** – Displays details for specific import runs.

These pages are wired together in [`App.tsx`](https://github.com/frappe/erpnext/blob/main/App.tsx), which configures the `BrowserRouter`, `FrappeProvider` (for data and SWR caching), `ThemeProvider`, and `TooltipProvider`. The entry point [`main.tsx`](https://github.com/frappe/erpnext/blob/main/main.tsx) bootstraps the React tree, injects Frappe boot context, and wraps the root in `DirectionProvider` for RTL/LTR support.

## How the Layers Interact

The data flow follows a strict unidirectional path through the component tree:

```

main.tsx
   └─> App.tsx (Router + Providers)
         ├─> pages/BankReconciliation.tsx
         │     └─> components/features/BankReconciliation/BankReconciliationStatement.tsx
         │           ├─> UI primitives (Table, Dialog, Button, Tooltip)
         │           └─> custom hooks (useDocType, useCurrentCompany)
         └─> pages/BankStatementImporterContainer.tsx
               └─> components/features/BankStatementImporter/CSV/CSVImport.tsx
                     └─> UI primitives (FileDropzone, Input, Button)

```

**Dependency rules:**
- **Pages** only import **feature components**.
- **Feature components** only import **primitives** and hooks.
- **Primitives** remain independent of all business logic.

## Code Implementation Examples

### Using the Button Primitive

```tsx
import { Button } from '@/components/ui/button';
import { SettingsIcon } from 'lucide-react';

export const OpenSettings = () => (
  <Button variant="outline" isIconButton size="md">
    <SettingsIcon />
  </Button>
);

```

*Source:* The implementation in [`banking/src/components/ui/button.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/components/ui/button.tsx) exposes `variant`, `size`, and `isIconButton` props via `class-variance-authority`.

### Building a Settings Dialog

```tsx
import {
  SettingsDialog,
  SettingsTabs,
  SettingsTabGroup,
  SettingsTabItem,
  SettingsPanel,
  SettingsPanels,
} from '@/components/ui/settings-dialog';
import { Preferences } from './Preferences';
import MatchingRules from './MatchingRules';
import { SlidersVerticalIcon, ZapIcon } from 'lucide-react';

export const Settings = () => (
  <SettingsDialog defaultValue="preferences" onClose={() => console.log('closed')}>
    <SettingsTabs>
      <SettingsTabGroup header="Configuration">
        <SettingsTabItem icon={<SlidersVerticalIcon />} label="Preferences" value="preferences" />
        <SettingsTabItem icon={<ZapIcon />} label="Matching Rules" value="rules" />
      </SettingsTabGroup>
    </SettingsTabs>
    <SettingsPanels>
      <SettingsPanel value="preferences"><Preferences /></SettingsPanel>
      <SettingsPanel value="rules"><MatchingRules /></SettingsPanel>
    </SettingsPanels>
  </SettingsDialog>
);

```

*Source:* The dialog composition pattern is defined in [`banking/src/components/ui/settings-dialog.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/components/ui/settings-dialog.tsx) and consumed in [`banking/src/components/features/Settings/Settings.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/components/features/Settings/Settings.tsx).

### Page Composition and Routing

```tsx
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import BankReconciliation from '@/pages/BankReconciliation';
import BankStatementImporterContainer from '@/pages/BankStatementImporterContainer';

export const AppRoutes = () => (
  <BrowserRouter>
    <Routes>
      <Route index element={<BankReconciliation />} />
      <Route path="/statement-importer/*" element={<BankStatementImporterContainer />} />
    </Routes>
  </BrowserRouter>
);

```

*Source:* Route definitions and provider setup are located in [`banking/src/App.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/App.tsx).

## Key Files in the Architecture

| File | Role |
|------|------|
| [`banking/src/components/ui/button.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/components/ui/button.tsx) | Core button primitive with variant support via `cva` |
| [`banking/src/components/ui/settings-dialog.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/components/ui/settings-dialog.tsx) | Composite dialog component for tabbed settings interfaces |
| [`banking/src/components/features/Settings/Settings.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/components/features/Settings/Settings.tsx) | Feature component assembling primitives for bank configuration |
| [`banking/src/pages/BankReconciliation.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/pages/BankReconciliation.tsx) | Top-level route container for the main banking interface |
| [`banking/src/App.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/App.tsx) | Application bootstrap with router and global providers |
| [`banking/src/main.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/main.tsx) | Entry point mounting the React tree and context providers |
| [`banking/src/lib/utils.ts`](https://github.com/frappe/erpnext/blob/main/banking/src/lib/utils.ts) | `cn` helper function for Tailwind class name merging |

## Summary

- The **structure of Frappe UI components in the banking module** follows a three-layer architecture: atomic primitives, feature components, and page containers.
- **Atomic primitives** in `src/components/ui/*` wrap Radix UI with Tailwind and `class-variance-authority` for type-safe variants.
- **Feature components** in `src/components/features/*` compose primitives to implement banking-specific workflows like reconciliation and statement import.
- **Page containers** in `src/pages/*` handle routing and integrate features under global providers defined in [`App.tsx`](https://github.com/frappe/erpnext/blob/main/App.tsx) and [`main.tsx`](https://github.com/frappe/erpnext/blob/main/main.tsx).
- The **`cn` utility** in [`src/lib/utils.ts`](https://github.com/frappe/erpnext/blob/main/src/lib/utils.ts) standardizes class name concatenation across all components.

## Frequently Asked Questions

### What are atomic UI primitives in the ERPNext banking module?

Atomic UI primitives are low-level React components stored in `banking/src/components/ui/*` that wrap Radix UI elements with Tailwind CSS styling. They include `Button`, `Dialog`, `Tooltip`, and `Input` components, each supporting variants via `class-variance-authority` and forwarding refs for accessibility. These primitives are framework-agnostic and contain no banking business logic.

### How does the banking module handle component styling?

Styling is implemented through **Tailwind CSS** utility classes composed with the `cn` helper function from [`src/lib/utils.ts`](https://github.com/frappe/erpnext/blob/main/src/lib/utils.ts). Components use `class-variance-authority` (cva) to define variant combinations (like button sizes and themes) without duplicating CSS. This approach ensures consistent theming and supports both light and dark modes via the `ThemeProvider`.

### Where are the page routes defined in the banking UI?

Routes are defined in **[`banking/src/App.tsx`](https://github.com/frappe/erpnext/blob/main/banking/src/App.tsx)**, which configures the `BrowserRouter` and `Routes` components from `react-router-dom`. Page components in `banking/src/pages/*` (such as [`BankReconciliation.tsx`](https://github.com/frappe/erpnext/blob/main/BankReconciliation.tsx) and [`BankStatementImporterContainer.tsx`](https://github.com/frappe/erpnext/blob/main/BankStatementImporterContainer.tsx)) are mapped to paths here. The entry point [`main.tsx`](https://github.com/frappe/erpnext/blob/main/main.tsx) wraps the entire application in necessary providers like `FrappeProvider` and `DirectionProvider`.

### What is the purpose of the `cn` utility in [`src/lib/utils.ts`](https://github.com/frappe/erpnext/blob/main/src/lib/utils.ts)?

The `cn` function is a utility that merges Tailwind CSS class names using `clsx` and `tailwind-merge`. It prevents conflicting class names when conditionally applying styles and ensures the correct utility classes take precedence. This function is imported across all UI primitives to maintain consistent and predictable styling throughout the banking module.