@msflib/react-components¶
Shared UI component library covering form building (FormBuilder), data tables, a chat UI kit (including Markdown message rendering and an "agent thinking" status pattern), drag-and-drop lists, tree views, Google auth buttons, resizable panes, skeleton loaders, an OTP input, and custom SVG icons. Install once and pull in only what you use via subpath imports.
Imports¶
Every component is available from the package root (@msflib/react-components),
and also from its own subpath:
import { FormBuilder } from '@msflib/react-components';
// or
import FormBuilder from '@msflib/react-components/formbuilder';
Both resolve to the same component and both keep working — the root import
is unchanged and not deprecated. The subpath import only pulls that
component's own dependencies instead of every component's combined
dependencies, which matters in bundler-based dev servers (e.g. Next.js
webpack/Turbopack) that don't tree-shake in development: importing from the
root there compiles the full dependency graph (MUI X Data Grid, @dnd-kit,
etc.) on first load even if you only render one component. Vite-based dev
servers (Storybook, most playgrounds) aren't affected either way, since
they pre-bundle dependencies once at startup rather than per route.
Available subpaths: ./formbuilder, ./table, ./chat-ui, ./markdown,
./draggable, ./tree-view, ./google-auth, ./resizable-pane,
./skeleton, ./otp-input, ./customsvg.
Component Breakdown¶
FormBuilder — ./formbuilder¶
Renders a form from a declarative array of field definitions instead of hand-wiring each input.
import FormBuilder from '@msflib/react-components/formbuilder';
<FormBuilder
elements={[
{ id: 'email', name: 'email', label: 'Email', dType: 'string', eType: 'email' },
{ id: 'password', name: 'password', label: 'Password', dType: 'string', eType: 'password' },
]}
formData={formData}
setFormData={setFormData}
onSubmit={(data, reset) => { /* submit, then optionally reset() */ }}
/>
FormBuilderProps
| Prop | Type | Required | Description |
|---|---|---|---|
elements |
FormElement[] |
Yes | Field definitions — see below. |
onSubmit |
(data: any, reset: () => void) => void |
Yes | Called with the current form data; call reset() to clear the form. |
formData |
any |
Yes | Controlled form state object. |
setFormData |
(data: any) => void |
No | Setter for formData; FormBuilder calls it on every field change. |
resetFormOnSubmit |
boolean |
No | Auto-reset after a successful submit. |
loadingState |
boolean |
No | Disables inputs / shows a loading state on submit buttons. |
layout |
React.ComponentType<LayoutProps> |
No | Supply your own layout component instead of the default flex-wrap grid. Receives { FormField, elements, formData, setFormData, isMobile, loading }. |
FormElement (each entry in elements)
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique identifier for the element. |
label |
ReactNode |
Yes | Field label. |
name |
string |
Yes | Form data key. |
dType |
string |
Yes | Data type (string, number, etc.) — drives the fallback renderer when eType isn't set. |
eType |
string |
No | Input renderer: text, password, email, number, date, datetime, textarea, select, file, checkbox, radio, autocomplete, searchlistmenu, taginput, switch, upload, otp, button. |
placeholder |
string |
No | Input placeholder text. |
validation |
any |
No | react-hook-form validation rules. |
width |
number |
No | Field width as a percentage. |
helperText |
string |
No | Helper text below the field. |
disabled |
boolean |
No | Disables the field. |
mData |
object |
No | Field-specific config — see below. |
FormElement.mData
| Field | Type | Description |
|---|---|---|
disabled / required / readOnly |
boolean |
Field state flags. |
align / variant / color |
string |
MUI-style visual options; color is a MUI palette key. |
rows |
number |
Textarea row count. |
accept |
string |
File input accept filter. |
select / options |
boolean / OptionItem[] |
Renders as a select with the given { value, label } options. |
width |
number |
Overrides the top-level width. |
preview_upload |
boolean |
Shows an image preview for upload fields. |
isCustomLabel / label_style |
boolean / CSSProperties |
Use a custom label style instead of the default MUI label. |
inputProps |
any |
Passed straight through to the underlying MUI input. |
href |
string |
For link-style button elements. |
startIcon / endIcon |
IconType |
{ icons: Record<string, ReactNode>, clickBehavior?: 'none' \| 'toggle' \| (() => void) } — icons rendered at the start/end of the input. endIcon defaults to a password show/hide toggle for password fields if omitted. |
searchPlaceholder |
string |
Placeholder for searchlistmenu fields. |
sx |
SxProps |
MUI sx object — see Styling below. |
onClick |
(event) => void |
Click handler, mainly for button elements. |
otpLength / otpType / mask |
number / 'numeric' \| 'alphanumeric' / boolean |
Only for eType: 'otp' — see OTP Input. |
Styling — per-field visual customisation goes through mData.sx (a plain MUI sx object), not by forking the component. From this repo's playground (apps/playground/styles/form.styles.ts):
export const baseMData = {
sx: {
width: '100%',
'& .MuiInputBase-root': {
borderRadius: '12px',
'& .MuiOutlinedInput-notchedOutline': { borderColor: 'rgba(0, 0, 0, 0.1)' },
'&.Mui-focused .MuiOutlinedInput-notchedOutline': { borderColor: '#4f46e5', borderWidth: '2px' },
},
},
isCustomLabel: true,
label_style: { marginBottom: '6px' },
};
export const buttonStyle = (color = '#4f46e5') => ({
borderRadius: '12px',
backgroundColor: color,
'&:hover': { backgroundColor: '#4338ca' },
});
Spread it into each field's mData, and into a button element's sx:
{ id: 'email', name: 'email', label: 'Email', dType: 'string', eType: 'email', mData: { ...baseMData } },
{ id: 'submit', name: 'submit', label: 'Sign in', dType: 'string', eType: 'button', mData: { sx: buttonStyle() } },
See the full file for dark-mode overrides via @media (prefers-color-scheme: dark) and a .dark & selector for class-based theming.
Table — ./table¶
A MUI X Data Grid wrapper (TableWidget) with built-in search, row actions menu, and optional drag-to-reorder.
import TableWidget from '@msflib/react-components/table';
<TableWidget
rows={rows}
columns={columns}
enableSearch
checkboxSelection
onRowClick={(params) => console.log(params.row)}
/>
| Prop | Type | Required | Description |
|---|---|---|---|
rows |
any[] |
Yes | Standard MUI X Data Grid rows. |
columns |
any[] |
Yes | Standard MUI X Data Grid column definitions. |
pageSize |
number |
No | Rows per page. Defaults to 5. |
pageSizeOptions |
number[] |
No | Options shown in the page-size selector. |
loading |
boolean |
No | Shows the grid's loading state. |
onRowClick |
(params: any) => void |
No | Fired when a row is clicked. |
onRowSelectionModelChange |
(newSelection: any) => void |
No | Fired when the selection changes. |
checkboxSelection |
boolean |
No | Adds a selection checkbox column. |
enableSearch |
boolean |
No | Shows a built-in search box that filters rows. |
tableTitle |
string |
No | Title shown next to the search box. |
menuItem |
boolean |
No | Adds a per-row "..." actions column. Must be true for menuItems/handleMenuClick to have any effect. |
menuItems |
MenuActionItem[] |
No | { key, label } options shown in the per-row actions menu. |
handleMenuClick |
(item: MenuActionItem, selectedRow: any) => void |
No | Called when a menu action is chosen. |
draggable |
boolean |
No | Enables drag-to-reorder rows. |
onRowsReorder |
(rows: any[]) => void |
No | Called with the new row order after a drag. |
autoHeight |
boolean |
No | Grid height fits its content instead of filling the container. |
styles |
StyleProps |
No | Per-part sx overrides: header, body, cell, row, rowHover, headers, root. |
Styling — pass a styles object targeting each part of the grid. From the playground (apps/playground/app/(public)/docs/tablewidget/table.styles.ts):
export const adaptiveTableStyles = {
headers: { backgroundColor: '#f8fafc', color: '#111111' },
row: { color: '#111111' },
rowHover: { backgroundColor: '#f5f5f5' },
root: {
borderRadius: '14px',
border: '1px solid #e5e7eb',
'& .MuiDataGrid-cell': { borderBottomColor: '#f1f5f9' },
},
};
<TableWidget rows={rows} columns={columns} styles={adaptiveTableStyles} />
See the full file for the paired dark-mode values (.dark & selectors) for every part.
ChatBox — ./chat-ui¶
A full chat UI: message list, input box, suggestions, feedback actions, file upload, and an auto-scroll-to-bottom behavior.
import ChatBox from '@msflib/react-components/chat-ui';
<ChatBox
messages={messages}
value={draft}
onChange={setDraft}
onSend={(text) => sendMessage(text)}
/>
| Prop | Type | Required | Description |
|---|---|---|---|
messages |
ChatMessage[] |
Yes | { id, role, content, avatar?, createdAt?, suggestions?, file?, files? } — content is rendered as Markdown by default when it's a string. |
value |
string |
No | Controlled input box value. |
onChange |
(value: string) => void |
No | Input box change handler. |
placeholder |
string |
No | Input box placeholder. |
disabled |
boolean |
No | Disables the input box and send button. |
roleMap |
Record<string, 'user' \| 'assistant' \| 'system'> |
No | Maps your own role strings to the three layout roles if they don't match directly. |
onSend |
(value: string) => void |
No | Called when the user sends a message. |
onUpload |
() => void |
No | Called when the attach-file button is clicked. |
onSuggestionClick |
(suggestion: ChatSuggestion) => void |
No | Called when a suggestion chip is clicked. |
onFeedback |
(message: ChatMessage, action: ChatFeedbackAction) => void |
No | Called when a feedback button is clicked. |
className / bodyClassName / messageClassName |
string |
No | Class names for the outer container, message list, and individual bubbles. |
textFieldProps |
TextFieldProps |
No | Props passed straight through to the underlying MUI TextField. |
textFieldSx |
SxProps |
No | sx override for the input box. |
autoScroll |
boolean |
No | Auto-scrolls to the latest message. |
scrollBehavior |
ScrollBehavior |
No | 'auto' \| 'smooth', passed to the scroll call. |
scrollThreshold |
number |
No | Distance (px) from the bottom within which auto-scroll still kicks in. |
allowFeedback |
boolean |
No | Shows per-message feedback buttons. |
feedbackActions |
ChatFeedbackAction[] |
No | { name, icon, label? } — the feedback buttons to show. |
displayAvatar |
boolean |
No | Shows each message's avatar. |
icons |
ChatBoxIcons |
No | { sendIcon?, attachmentIcon? } overrides. |
messageMaxWidth |
CSSProperties['maxWidth'] |
No | Max width of a message column (avatar + bubble + actions). Defaults to '70%'. |
disableMarkdown |
boolean |
No | Renders string content as plain text instead of Markdown. |
styles |
object |
No | Per-part CSSProperties: root, body, footer, bubble, assistantBubble, userBubble, systemBubble, sendButton, uploadButton, feedbackButton, suggestionButton, avatar, dateSeparator, timestamp. |
renderMessage |
(message: ChatMessage, role: ChatComponentRole) => ReactNode |
No | Fully override how a single message renders (see renderChatMessage.tsx example in the playground app). |
renderMessageCard |
(message: ChatMessage, role: ChatComponentRole) => ReactNode |
No | Override just the card/bubble wrapper, keeping default content layout. |
renderHeader |
ReactNode \| (() => ReactNode) |
No | Custom header above the message list. |
renderFooterActions |
() => ReactNode |
No | Custom actions rendered in the footer, alongside the input box. |
Styling — two levels: the granular styles object (per-part CSSProperties — bubble, userBubble, assistantBubble, sendButton, avatar, etc.) for most visual tweaks, and className/bodyClassName/messageClassName/textFieldSx for container-level and input-box styling. messageMaxWidth controls bubble width directly. For full control over a bubble's markup (not just its style), use renderMessage.
<ChatBox
messages={messages}
bodyClassName="gap-4 px-6"
messageMaxWidth="80%"
textFieldSx={{ borderRadius: '12px' }}
styles={{
userBubble: { backgroundColor: '#4f46e5', color: '#fff' },
assistantBubble: { backgroundColor: '#f4f4f5' },
}}
/>
Markdown — ./markdown¶
The MarkdownMessage renderer ChatBox uses internally for string message content — use it standalone if you're building your own chat layout instead of using ChatBox.
import { MarkdownMessage } from '@msflib/react-components/markdown';
<MarkdownMessage content={responseText} />
Handles code blocks with syntax highlighting and a copy button, tables (with a copy-as-CSV affordance), and math delimiter normalization.
| Prop | Type | Required | Description |
|---|---|---|---|
content |
ReactNode \| MarkdownContentPart[] |
Yes | The message content. Also accepts a LangChain-style content-parts array ([{ text: '...' }, ...]) — joined to plain text automatically. |
disableMarkdown |
boolean |
No | Renders content as plain text instead of Markdown. |
disableGfm |
boolean |
No | Disables GitHub-Flavored Markdown (tables, strikethrough, task lists). |
enableMath |
boolean |
No | Enables math delimiter rendering ($...$, $$...$$). |
disableSyntaxHighlight |
boolean |
No | Disables code-block syntax highlighting. |
syntaxTheme |
Record<string, CSSProperties> |
No | Highlighting theme for code blocks — pass the package's own oneLight/oneDark, or your own. |
disableCopy |
boolean |
No | Removes the copy button from code blocks and tables. |
styles |
MarkdownMessageStyles |
No | Per-element-type CSSProperties: root, paragraph, heading1/heading2/heading3, list, listItem, link, blockquote, table, tableHeadCell, tableCell, codeInline, codeBlock, codeBlockWrapper, image. |
components |
Partial<Components> |
No | Override individual react-markdown element renderers directly. |
Styling — the styles object above covers every element type Markdown can render. For syntax highlighting specifically, use syntaxTheme.
import { MarkdownMessage, oneDark } from '@msflib/react-components/markdown';
<MarkdownMessage
content={responseText}
styles={{ link: { color: '#4f46e5' } }}
syntaxTheme={oneDark}
/>
Draggable — ./draggable¶
A sortable list (built on @dnd-kit) for reordering an arbitrary array of items.
import DraggableList from '@msflib/react-components/draggable';
// Each item must have an `id: string` — it's used as the drag key.
<DraggableList
items={items} // e.g. [{ id: '1', label: 'First' }, { id: '2', label: 'Second' }]
onDragEnd={(reordered) => setItems(reordered)}
renderItem={(item) => <div>{item.label}</div>}
/>
| Prop | Type | Required | Description |
|---|---|---|---|
items |
T[] (each item extends { id: string }) |
Yes | The list to render and reorder. |
onDragEnd |
(reorderedItems: T[]) => void |
Yes | Called with the new order after a drag completes. |
renderItem |
(item: T) => ReactNode |
Yes | Renders each item's content. |
itemClassName |
string |
No | Class name applied to each draggable row wrapper. |
Styling — itemClassName styles each draggable row wrapper; style the item's own content however you like inside renderItem.
TreeView — ./tree-view¶
A file/folder-style tree, with optional file-type icons, multi-select, and controlled or uncontrolled expansion/selection.
import { TreeView } from '@msflib/react-components/tree-view';
<TreeView
items={tree}
getItemId={(item) => item.id}
getItemLabel={(item) => item.name}
getItemChildren={(item) => item.children}
showIcons
/>
| Prop | Type | Required | Description |
|---|---|---|---|
items |
TItem[] |
Yes | Root-level items. Defaults to { id, label, children?, expandable?, disabled?, extension?, icon? } if you don't supply your own TItem shape. |
getItemId / getItemLabel / getItemChildren |
functions | No | Accessors for a custom TItem shape. Default to reading id/label/children directly. |
isItemExpandable / isItemDisabled |
(item: TItem) => boolean |
No | Override expandability/disabled state per item. |
getItemIcon / getItemExtension |
functions | No | Per-item icon override, and the file extension used to look up fileIconMap. |
showIcons |
boolean |
No | Shows folder/file-type icons beside each item. Off by default. |
folderIcon |
ReactNode |
No | Icon for expandable items when showIcons is true, unless getItemIcon returns one for that item. |
fileIconMap |
Record<string, ReactNode> |
No | Extension (no leading dot) → icon, merged over the built-in default map. Only consulted for non-expandable items. |
itemGap |
number \| string |
No | Vertical spacing between sibling items at every depth. Defaults to 0. |
indentSize |
'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| number |
No | Horizontal indent per depth level (xs=12, sm=16, md=20, lg=28, xl=36, or a raw px number). Defaults to 'md'. |
icons |
TreeViewIcons |
No | { expandIcon?, collapseIcon?, leafIcon?, loadingIcon? }. |
styles |
TreeViewStyles |
No | Per-part CSSProperties: root, group, item, itemSelected, itemDisabled, itemFocused, icon, typeIcon, label, endAdornment. |
className / itemClassName / groupClassName / labelClassName / endAdornmentClassName |
string |
No | Class names for the root, each item, each children group, each label, and each end adornment. |
expandedItems / defaultExpandedItems / onExpandedItemsChange |
— | No | Controlled or uncontrolled expansion state. |
onItemExpansionToggle |
(event, itemId, isExpanded) => void |
No | Fired when a single item's expansion is toggled. |
multiSelect |
boolean |
No | Allow selecting more than one item. |
selectedItems / defaultSelectedItems / onSelectedItemsChange |
TreeViewSelection (string \| string[] \| null) |
No | Controlled or uncontrolled selection state. |
onItemClick / onItemFocus |
(event, itemId) => void |
No | Click/focus handlers. |
isItemLoading |
(itemId: string) => boolean |
No | Shows icons.loadingIcon for an item (e.g. lazy-loaded children). |
renderLabel |
(meta: TreeViewItemMeta<TItem>) => ReactNode |
No | Fully override a label's markup. |
renderItem |
(meta, defaultNode: ReactNode) => ReactNode |
No | Fully override an item's markup, with the default render available to wrap. |
renderItemEndAdornment |
(meta: TreeViewItemMeta<TItem>) => ReactNode |
No | Right-aligned content per item (e.g. a lock icon, a VCS status letter). |
aria-label / aria-labelledby |
string |
No | Accessibility labeling for the root element. |
Styling — styles: TreeViewStyles for per-part overrides, plus className/itemClassName/groupClassName/labelClassName for targeted CSS classes, and itemGap/indentSize for spacing. Use renderLabel/renderItem to fully customize a row's markup instead of fighting the defaults with CSS.
Google Auth — ./google-auth¶
A "Sign in with Google" button plus the redirect/callback handling it needs.
import { GoogleAuthButton, GoogleAuthHandler } from '@msflib/react-components/google-auth';
<GoogleAuthButton apiBaseUrl="https://api.example.com" />
// On your redirect/callback route:
<GoogleAuthHandler
apiBaseUrl="https://api.example.com"
onSuccess={(data) => { /* store token, redirect, etc. */ }}
/>
GoogleAuthButtonProps (extends MUI's ButtonProps — sx, variant, color, fullWidth, etc. all work directly)
| Prop | Type | Required | Description |
|---|---|---|---|
apiBaseUrl |
string |
Yes | Base URL the login redirect is built against. |
label |
string |
No | Button text. |
redirectUrl |
string |
No | Where Google redirects back to after sign-in. |
onRedirect |
() => void |
No | Called right before the redirect to Google happens. |
googleLoginPath |
string |
No | API path that kicks off the Google login flow. |
as |
React.ElementType |
No | Render as a different element/component instead of MUI's Button. |
iconSize |
number |
No | Size of the Google icon. |
GoogleAuthHandlerProps (place on your redirect/callback route)
| Prop | Type | Required | Description |
|---|---|---|---|
apiBaseUrl |
string |
Yes | Base URL used to complete the callback exchange. |
onSuccess |
(data: any) => void |
Yes | Called with the authenticated session data. |
redirectUrl |
string |
No | Where to send the user after a successful callback. |
onError |
(error: any) => void |
No | Called if the callback exchange fails. |
modalComponent |
ReactNode |
No | Custom "signing you in..." UI shown while the callback is processed. |
There's also a useGoogleCallback(options) hook (UseGoogleCallbackOptions: apiBaseUrl, redirectUrl?, callbackEndpoint?, onSuccess, onError?) if you want to handle the callback yourself instead of using GoogleAuthHandler.
Styling — GoogleAuthButtonProps extends MUI's ButtonProps directly, so sx, variant, color, fullWidth, etc. all work exactly as they do on a plain MUI Button.
<GoogleAuthButton apiBaseUrl="https://api.example.com" variant="outlined" sx={{ borderRadius: '12px' }} />
Resizable Pane — ./resizable-pane¶
A horizontal or vertical split pane with draggable (and keyboard-operable) dividers.
import { ResizablePane } from '@msflib/react-components/resizable-pane';
<ResizablePane
direction="horizontal"
panes={[
{ initialSize: '30%', minSize: 200, render: () => <Sidebar /> },
{ render: () => <MainContent /> },
]}
/>
ResizablePaneProps
| Prop | Type | Required | Description |
|---|---|---|---|
panes |
PaneConfig[] |
Yes | The panes to render, in order. |
direction |
'horizontal' \| 'vertical' |
No | Split direction. Defaults to 'horizontal'. |
style |
CSSProperties |
No | Styles the container. |
paneDividerStyle |
CSSProperties |
No | Styles each drag handle between panes. |
className |
string |
No | Class name on the root. |
PaneConfig (each entry in panes)
| Field | Type | Description |
|---|---|---|
render |
() => ReactNode |
The pane's content. |
initialSize |
number \| \${number}%`| Starting size (px or percentage). Defaults to200`. |
|
minSize / maxSize |
number \| \${number}%`| Resize bounds.minSizedefaults to50`. Dividers are also keyboard-operable (arrow keys move by a fixed step within these bounds). |
Styling — style sets the container's CSS, paneDividerStyle styles the drag handles between panes, and className applies to the root.
<ResizablePane
panes={panes}
style={{ borderRadius: '12px', overflow: 'hidden' }}
paneDividerStyle={{ backgroundColor: '#e5e7eb', width: '2px' }}
/>
Skeleton — ./skeleton¶
Loading-state placeholders: AppSkeleton for a generic block, SkeletonLoaderWrapper to swap a layout component for its skeleton version while loading is true, and SkeletonCardLayout for a ready-made card skeleton.
import { SkeletonLoaderWrapper } from '@msflib/react-components/skeleton';
<SkeletonLoaderWrapper loading={isLoading} layout={CardLayout}>
<Card data={data} />
</SkeletonLoaderWrapper>
SkeletonLoaderWrapperProps
| Prop | Type | Required | Description |
|---|---|---|---|
loading |
boolean |
Yes | Shows the skeleton (via layout) while true; renders children once false. |
layout |
React.ComponentType<{ className?: string }> |
Yes | The skeleton shape to render while loading — typically a layout matching your real content's structure. |
children |
ReactNode |
Yes | The real content, rendered once loading is false. |
skeletonLength |
number |
No | Number of skeleton items to repeat (for list-style layouts). |
className |
string |
No | Class name on the wrapper. |
useSwiperSlide / slideClassName |
boolean / string |
No | Wrap each skeleton item in a Swiper slide, for carousel-style loading states. |
AppSkeletonProps — a generic skeleton block (also exposes AppSkeleton.Avatar as a sub-component)
| Prop | Type | Description |
|---|---|---|
children |
ReactNode |
Content rendered inside the block. |
className |
string |
Class name on the block. |
dataTestId |
string |
data-testid for testing. |
TextSkeletonProps (extends MUI's SkeletonProps)
| Prop | Type | Description |
|---|---|---|
lines |
number |
Number of skeleton text lines to render. |
SkeletonLayoutProps — used by SkeletonCardLayout and any custom layout you write
| Prop | Type | Description |
|---|---|---|
className |
string |
Class name on the layout root. |
Styling — className on AppSkeleton/SkeletonLoaderWrapper targets the wrapper; the skeleton's actual shape comes from the layout component you pass in (it renders that layout's structure in a skeleton state), so style it by styling your layout component.
OTP Input — ./otp-input¶
A segmented one-time-code input with auto-advance, backspace/arrow-key navigation, and paste support.
import OtpInput from '@msflib/react-components/otp-input';
<OtpInput length={6} onComplete={(code) => verifyOtp(code)} />
| Prop | Type | Description |
|---|---|---|
length |
number |
Number of boxes. Defaults to 6. |
value / defaultValue |
string |
Controlled or uncontrolled value. |
onChange |
(value: string) => void |
Fires on every change with the joined value. |
onComplete |
(value: string) => void |
Fires once every box is filled. |
type |
'numeric' \| 'alphanumeric' |
Restricts allowed characters per box. |
mask |
boolean |
Renders each box as a password field. |
disabled |
boolean |
Disables every box. |
error |
boolean |
Error state styling. |
autoFocus |
boolean |
Focuses the first box on mount. |
name |
string |
Renders a hidden input with the joined value, for plain form submission. |
gap |
number |
Spacing between boxes. |
boxSize |
number |
Size of each box. |
className / style |
string / CSSProperties |
Styling for the wrapper. |
ariaLabel |
string |
Accessibility label. |
Styling — className/style target the wrapper; gap and boxSize control spacing and box size directly rather than needing CSS overrides.
<OtpInput length={6} boxSize={48} gap={12} onComplete={verifyOtp} />
Custom SVG — ./customsvg¶
Renders an SVG file as an inline, stylable icon (so fill/stroke via props or CSS actually work, unlike an <img> tag).
import CustomSvg from '@msflib/react-components/customsvg';
<CustomSvg src="/icons/logo.svg" fill="currentColor" />
| Prop | Type | Required | Description |
|---|---|---|---|
src |
string |
Yes | URL/path to the SVG file. Fetched and inlined (not rendered as an <img>). |
className |
string |
No | Class name on the wrapping <span>. |
fill |
string |
No | Overrides the SVG's fill color. |
stroke |
string |
No | Overrides the SVG's stroke color. |
Styling — className for layout/sizing via CSS, plus fill/stroke props to recolor the icon directly (useful with currentColor to inherit the surrounding text color).