FSAI Design System
Components

Modal

Dialog overlay for focused tasks, confirmations, and forms.

Overview

The Modal component provides an overlay dialog with header, body, and footer sections. It supports custom sizing, fullscreen mode, actions (submit/button), and compound composition via primitives.

Basic Usage

import { Modal } from '@fsai/shared-ui';

<Modal
  isOpen={isOpen}
  handleClose={() => setIsOpen(false)}
  title="Edit Profile"
  action={{
    type: 'button',
    label: 'Save',
    onClick: handleSave,
  }}
>
  <p>Modal body content goes here.</p>
</Modal>

With Form Submit

Tie the action to a form using type: 'submit':

<Modal
  isOpen={isOpen}
  handleClose={() => setIsOpen(false)}
  title="Create Item"
  action={{
    type: 'submit',
    formId: 'create-item-form',
    label: 'Create',
  }}
>
  <form id="create-item-form" onSubmit={handleSubmit}>
    <Input label="Name" name="name" />
  </form>
</Modal>

Loading Action

<Modal
  isOpen={isOpen}
  handleClose={() => setIsOpen(false)}
  title="Saving..."
  action={{
    type: 'button',
    label: 'Save',
    onClick: handleSave,
    isLoading: isSaving,
  }}
>
  {children}
</Modal>

Compound Components (Primitives)

For advanced layouts, use the modal primitives directly:

import { Modal } from '@fsai/shared-ui';

<Modal.Root isOpen={isOpen} onClose={() => setIsOpen(false)}>
  <Modal.Backdrop />
  <Modal.Panel>
    <Modal.Header>
      <Modal.Title>Custom Layout</Modal.Title>
      <Modal.CloseButton />
    </Modal.Header>
    <Modal.Body>Content</Modal.Body>
    <Modal.Footer>
      <Button role="button" variant="secondary" onClick={() => setIsOpen(false)}>Cancel</Button>
      <Button role="button" variant="primary" onClick={handleSave}>Save</Button>
    </Modal.Footer>
  </Modal.Panel>
</Modal.Root>

Props

PropTypeDefaultDescription
isOpenbooleanRequired. Controls visibility
titleReactNodeRequired. Modal heading
childrenReactNodeRequired. Modal body content
handleClose() => voidCalled when modal should close
actionModalActionPrimary action button config
subTitleReactNodeSubtitle below the title
IconComponentTypeIcon in the header
widthstring | numberCustom width
maxWidthstring | numberMaximum width
heightstring | numberCustom height
maxHeightstring | numberMaximum height
fullscreenbooleanfalseExpands to fill the viewport
closeOnOutsideClickbooleantrueClose when clicking the backdrop
withPaddingbooleantrueApplies body padding
hideHeaderbooleanfalseHides the header section
secondaryLabelstring'Cancel'Secondary button label
footerReactNodeCustom footer content
zIndexnumber80CSS z-index

ModalAction

PropTypeDescription
type'button' | 'submit'Required. Action type
labelstringButton label
onClick() => voidClick handler (button type)
formIdstringForm ID to submit (submit type)
variantButtonVariantButton variant
isLoadingbooleanShows loading spinner
disabledbooleanDisables the action
iconIconNameAction button icon

Guidelines

  • Do always provide a clear title that describes the task
  • Do use closeOnOutsideClick for non-destructive modals
  • Do set closeOnOutsideClick={false} for forms with unsaved changes
  • Don't nest modals inside modals
  • Don't use modals for simple confirmations — use ConfirmActionModal instead
  • Don't use fullscreen mode on desktop unless the content genuinely requires it

On this page