@capyx/components-library
v0.0.22
Published
Shared React component library for Capyx applications
Readme
Capyx Components Library
The shared React component library for all Capyx applications.
Built with TypeScript and React 19, the library ships fully-typed ESM + CJS bundles. Components are opinionated about their UI stack (see Coupled Dependencies) so that every Capyx app has a consistent look and feel without having to re-configure the same set of libraries.
Architecture
The library is organised into two layers:
Components
Self-contained visual components (currently focused on form inputs, but not restricted to them):
- CheckInput - Checkbox input for boolean values
- DateInput - Date picker with formatted string output (using MUI DatePicker)
- FileInput - File upload with validation and preview
- RichTextInput - Rich text (WYSIWYG) editor using Quill. Grows with content by default; supports height-constrained / scrollable layouts (e.g. inside a modal)
- SelectInput - Dropdown selection for single/multiple options
- SwitchInput - Toggle switch for on/off values
- TagsInput - Tag management with MUI Autocomplete
- TextAreaInput - Multiline text input with auto-growing height
- TextInput - Single-line text input for various types (text, email, password, number, etc.)
Addons (Enhancement Wrappers)
Addons wrap existing components to add behaviour without modifying the base component:
- AutocompleteInput - Adds autocomplete/suggestions dropdown to text inputs
- CharacterCountInput - Adds character counting to text, textarea, or editor inputs
- EditorAddon - Wraps TextAreaInput to add rich text editing (ReactQuill)
- Editor (Legacy) - Standalone rich text editor (deprecated, use EditorAddon instead)
Coupled Dependencies
The packages below are production dependencies bundled with the library. Every consuming application must have compatible versions installed. They are considered part of the library's core stack and should not be removed or swapped lightly.
UI foundation
| Package | Version | Role |
|---|---|---|
| react | ^19 | Core framework |
| react-bootstrap | ^2 | Layout, forms, modals, and all general UI primitives |
| bootstrap | ^5 | CSS layer required by react-bootstrap |
Form management
| Package | Version | Role |
|---|---|---|
| react-hook-form | ^7 | Form state, validation, and field registration — deeply integrated into most input components |
MUI (advanced components)
| Package | Version | Role |
|---|---|---|
| @mui/material | ^7 | MUI component primitives (used by TagsInput, SelectInput, etc.) |
| @emotion/styled | ^11 | Mandatory styling engine for MUI |
| @mui/x-date-pickers | ^8 | Date picker used by DateInput |
Date & time
| Package | Version | Role |
|---|---|---|
| dayjs | ^1 | Date adapter required by @mui/x-date-pickers |
| dateformat | ^5 | Output formatting for DateInput |
Rich text
| Package | Version | Role |
|---|---|---|
| react-quill-new | ^3 | Quill WYSIWYG editor used by RichTextInput |
Utilities
| Package | Version | Role |
|---|---|---|
| lodash.debounce | ^4 | Debouncing used in AutocompleteInput |
Features
- ✅ React 19 compatible
- ✅ TypeScript with full type safety and auto-generated type declarations
- ✅ ESM and CommonJS dual module support
- ✅ react-hook-form integration for form state management and validation
- ✅ react-bootstrap for consistent styling
- ✅ Material-UI (MUI) for advanced components (DatePicker, Autocomplete)
- ✅ Standalone or form-integrated modes
- ✅ Consistent error handling
- ✅ Customizable styling
Installation
npm install @capyx/components-libraryPeer Dependencies
This library requires the following peer dependencies to be installed in your project:
npm install react react-dom react-hook-form react-bootstrap @mui/material @mui/x-date-pickers dayjs react-quill-new react-autosuggestTypeScript Support
This library is written in TypeScript and includes full type definitions. Type declarations are automatically provided when you install the package, giving you:
- Full IntelliSense and autocomplete in your IDE
- Type checking for component props
- Improved development experience with inline documentation
No additional @types packages are needed!
Usage
Basic Component Usage
import { TextInput, CheckInput, DateInput } from '@your-package/components-library';
import { FormProvider, useForm } from 'react-hook-form';
function MyForm() {
const methods = useForm();
return (
<FormProvider {...methods}>
<form>
<TextInput
name='username'
label='Username'
required
maxLength={50}
/>
<CheckInput
name='terms'
label='I agree to terms'
required
/>
<DateInput
name='birthdate'
label='Date of Birth'
required
/>
</form>
</FormProvider>
);
}RichTextInput
RichTextInput is a Quill-based WYSIWYG editor that grows with its content by default.
Basic usage
import { RichTextInput } from '@your-package/components-library';
function MyForm() {
const [content, setContent] = useState('');
return (
<RichTextInput
value={content}
onChange={setContent}
maxLength={5000}
/>
);
}Height-constrained layout (e.g. inside a modal)
By default the editor has no maximum height. In a constrained context — such as a Bootstrap Modal — you want the editor to grow until the modal fills the viewport, then scroll inside the editor instead of overflowing the page.
Use the constrainHeight prop together with a style that sets the height. Three things are required:
<Modal scrollable>— Bootstrap caps the modal body at the available viewport height.d-flex flex-column overflow-hiddenon<Modal.Body>— turns the body into a flex column that constrains its children to the available height.constrainHeight+style={{ flex: 1, minHeight: 0 }}on<RichTextInput>—constrainHeightactivates the CSS flex chain through Quill's internal DOM nodes;flex: 1fills the remaining space;minHeight: 0is required in a flex context so the wrapper can shrink below its natural size.
import { RichTextInput } from '@your-package/components-library';
import { Modal, Button } from 'react-bootstrap';
function MyModal({ show, onHide }) {
const [content, setContent] = useState('');
return (
<Modal show={show} onHide={onHide} scrollable>
<Modal.Header closeButton>
<Modal.Title>Edit content</Modal.Title>
</Modal.Header>
{/* flex column + overflow-hidden constrains children to available height */}
<Modal.Body className="d-flex flex-column overflow-hidden">
<RichTextInput
value={content}
onChange={setContent}
constrainHeight
{/* flex:1 fills remaining space; minHeight:0 enables overflow */}
style={{ flex: 1, minHeight: 0 }}
/>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={onHide}>Cancel</Button>
<Button variant="primary" onClick={onHide}>Save</Button>
</Modal.Footer>
</Modal>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | '' | Current editor content as an HTML string |
| onChange | (value: string) => void | — | Fired on every content change |
| readonly | boolean | false | Hides the toolbar and disables editing |
| maxLength | number | — | Hard character limit (measured against the raw HTML string). Shows a counter below the editor |
| isInvalid | boolean | false | Applies Bootstrap is-invalid border styling |
| formats | FormatType[] | ['header','bold','italic','underline','list'] | Allowed Quill formats |
| style | CSSProperties | — | Inline styles on the outer wrapper div. Use to constrain height in a flex context |
| wrapperClassName | string | — | Extra CSS class names on the outer wrapper div |
| constrainHeight | boolean | false | Activates internal flex chain so the editor scrolls inside itself. Requires a height to be set via style (e.g. style={{ flex: 1, minHeight: 0 }} or style={{ height: '40vh' }}) |
TextAreaInput
TextAreaInput is a multiline text input that auto-grows with its content using a row-counting approach. There are no inline style height overrides, so consumers can freely customise appearance with plain CSS.
Basic usage
import { TextAreaInput } from '@capyx/components-library';
import { FormProvider, useForm } from 'react-hook-form';
function MyForm() {
const methods = useForm();
return (
<FormProvider {...methods}>
<TextAreaInput name="notes" label="Notes" placeholder="Enter notes…" />
</FormProvider>
);
}Controlling row height
{/* Starts at 5 rows, grows without limit */}
<TextAreaInput name="bio" label="Biography" minRows={5} />
{/* Starts at 2 rows, scrolls internally after 8 rows */}
<TextAreaInput name="summary" label="Summary" maxRows={8} />
{/* Fixed band: always between 3 and 6 rows */}
<TextAreaInput name="notes" label="Notes" minRows={3} maxRows={6} />Props
| Prop | Type | Default | Description |
|---|---|---|---|
| name | string | — | Field name for form registration |
| label | string | — | Label text |
| required | boolean | false | Marks the field as required |
| maxLength | number | — | Maximum number of characters allowed |
| placeholder | string | — | Placeholder text |
| value | string | — | Controlled value (standalone mode) |
| onChange | (value: string) => void | — | Change callback |
| disabled | boolean | false | Disables the field |
| isReadOnly | boolean | false | Makes the field read-only |
| isPlainText | boolean | false | Renders as plain text (no border) |
| controlSize | 'sm' \| 'lg' | — | Bootstrap size variant |
| debounceMs | number | — | Debounce delay in milliseconds for onChange |
| minRows | number | 2 | Minimum number of visible rows |
| maxRows | number | — | Maximum rows before the textarea scrolls internally. Unset = grows without limit |
Using Addons
Autocomplete Addon
import { TextInput, AutocompleteInput } from '@your-package/components-library';
function MyForm() {
const countries = ['USA', 'Canada', 'UK', 'Australia'];
return (
<AutocompleteInput suggestions={countries}>
<TextInput
name='country'
label='Country'
placeholder='Type to search...'
/>
</AutocompleteInput>
);
}Character Count Addon
import { TextInput, CharacterCountInput } from '@your-package/components-library';
function MyForm() {
return (
<CharacterCountInput>
<TextInput
name='bio'
label='Biography'
maxLength={200}
/>
</CharacterCountInput>
);
}Editor Addon (Rich Text)
import { EditorAddon } from '@your-package/components-library';
import { Controller, useFormContext } from 'react-hook-form';
function MyForm() {
const { control } = useFormContext();
return (
<Controller
name='content'
control={control}
render={({ field }) => (
<EditorAddon
value={field.value}
onChange={field.onChange}
maxLength={5000}
/>
)}
/>
);
}Combining Addons
Addons can be nested to combine their functionality:
import { TextInput, AutocompleteInput, CharacterCountInput } from '@your-package/components-library';
function MyForm() {
const cities = ['New York', 'Los Angeles', 'Chicago', 'Houston'];
return (
<CharacterCountInput>
<AutocompleteInput suggestions={cities}>
<TextInput
name='city'
label='City'
maxLength={50}
/>
</AutocompleteInput>
</CharacterCountInput>
);
}Component API
Common Props
All components support these common props when used with react-hook-form:
name: string(required) - Field name for form registrationlabel?: string- Label textrequired?: boolean- Whether the field is requireddisabled?: boolean- Whether the field is disabled
Standalone Mode
All components can be used without react-hook-form by providing value and onChange props:
<TextInput
name='standalone'
value={value}
onChange={(newValue) => setValue(newValue)}
/>Styling
Components use react-bootstrap and MUI components, making them easy to customize:
- Override Bootstrap variables for global theming
- Use MUI theme provider for MUI components
- Add custom className props (where supported)
TypeScript Support
All components are fully typed with TypeScript. Import types as needed:
import type { TextAreaInputProps, CheckInputProps } from '@your-package/components-library';React 19 Compatibility
This library is built and tested with React 19. All components use modern React patterns:
- Functional components with hooks
- No deprecated lifecycle methods
- No unsafe React APIs
- Full concurrent mode support
Contributing
When adding new components:
- Components go in
lib/components/- base input types - Addons go in
lib/addons/- wrappers that enhance components - All components must support both react-hook-form and standalone modes
- Export types alongside components
- Update index files for proper exports
