# ModalFooter
**📖 Live documentation:** https://cds.coinbase.com/components/overlay/ModalFooter/
A footer component for Modal.
## Import
```tsx
import { ModalFooter } from '@coinbase/cds-web/overlays/modal/ModalFooter'
```
## Examples
ModalFooter provides a consistent action area for [Modal](/components/overlay/Modal). It accepts `primaryAction` and optional `secondaryAction` buttons with responsive layout behavior.
### Basics
Pass a `primaryAction` button to create a simple footer. The footer automatically displays a top border.
```jsx live
function BasicExample() {
const [visible, setVisible] = useState(false);
return (
<>
setVisible(false)} visible={visible}>
Your changes have been saved.
setVisible(false)}>Done} />
>
);
}
```
### Two Actions
Add a `secondaryAction` for cancel/dismiss patterns. On larger screens, buttons display side-by-side. On smaller screens, they stack vertically with the primary action on top.
```jsx live
function TwoActionsExample() {
const [visible, setVisible] = useState(false);
return (
<>
setVisible(false)} visible={visible}>
Are you sure you want to proceed with this action?
setVisible(false)}>Confirm}
secondaryAction={
}
/>
>
);
}
```
### Destructive Actions
Use `variant="negative"` on the primary button for destructive actions.
```jsx live
function DestructiveExample() {
const [visible, setVisible] = useState(false);
return (
<>
setVisible(false)} visible={visible}>
This action cannot be undone. All your data will be permanently deleted.
setVisible(false)} variant="negative">
Delete
}
secondaryAction={
}
/>
>
);
}
```
### Loading State
Show a loading state on the primary button while an action is processing.
```jsx live
function LoadingExample() {
const [visible, setVisible] = useState(false);
const [loading, setLoading] = useState(false);
const handleSave = () => {
setLoading(true);
setTimeout(() => {
setLoading(false);
setVisible(false);
}, 2000);
};
return (
<>
setVisible(false)} visible={visible}>
Your changes will be saved to the server.
Save
}
secondaryAction={
}
/>
>
);
}
```
## Props
| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `primaryAction` | `ReactElement & { variant?: ButtonVariant \| undefined; disabled?: boolean \| undefined; loading?: boolean \| undefined; progressCircleSize?: number \| undefined; transparent?: boolean \| undefined; block?: boolean \| undefined; compact?: boolean \| undefined; children: ReactNode; start?: ReactNode; startIcon?: IconName \| undefined; startIconActive?: boolean \| undefined; end?: ReactNode; endIcon?: IconName \| undefined; endIconActive?: boolean \| undefined; flush?: start \| end \| undefined; name?: string \| undefined; noScaleOnPress?: boolean \| undefined; numberOfLines?: number \| undefined; } & Omit & { onClick?: MouseEventHandler \| undefined; }, string \| JSXElementConstructor>` | Yes | `-` | Primary action button |
| `alignContent` | `ResponsiveProp` | No | `-` | - |
| `alignItems` | `ResponsiveProp` | No | `-` | - |
| `alignSelf` | `ResponsiveProp` | No | `-` | - |
| `as` | `div` | No | `-` | The underlying element or component the polymorphic component will render. Changing as also changes the inherited native props (e.g. href for as=a) and the expected ref type. |
| `aspectRatio` | `ResponsiveProp` | No | `-` | - |
| `background` | `ResponsiveProp` | No | `-` | - |
| `borderBottomLeftRadius` | `ResponsiveProp` | No | `-` | - |
| `borderBottomRightRadius` | `ResponsiveProp` | No | `-` | - |
| `borderBottomWidth` | `ResponsiveProp` | No | `-` | - |
| `borderColor` | `ResponsiveProp` | No | `-` | - |
| `borderEndWidth` | `ResponsiveProp` | No | `-` | - |
| `borderRadius` | `ResponsiveProp` | No | `-` | - |
| `borderStartWidth` | `ResponsiveProp` | No | `-` | - |
| `borderTopLeftRadius` | `ResponsiveProp` | No | `-` | - |
| `borderTopRightRadius` | `ResponsiveProp` | No | `-` | - |
| `borderTopWidth` | `ResponsiveProp` | No | `-` | - |
| `borderWidth` | `ResponsiveProp` | No | `-` | - |
| `bordered` | `boolean` | No | `-` | Add a border around all sides of the box. |
| `borderedBottom` | `boolean` | No | `-` | Add a border to the bottom side of the box. |
| `borderedEnd` | `boolean` | No | `-` | Add a border to the trailing side of the box. |
| `borderedHorizontal` | `boolean` | No | `-` | Add a border to the leading and trailing sides of the box. |
| `borderedStart` | `boolean` | No | `-` | Add a border to the leading side of the box. |
| `borderedTop` | `boolean` | No | `-` | Add a border to the top side of the box. |
| `borderedVertical` | `boolean` | No | `-` | Add a border to the top and bottom sides of the box. |
| `bottom` | `ResponsiveProp>` | No | `-` | - |
| `color` | `ResponsiveProp` | No | `-` | - |
| `columnGap` | `ResponsiveProp` | No | `-` | - |
| `dangerouslySetBackground` | `string` | No | `-` | - |
| `display` | `ResponsiveProp` | No | `-` | - |
| `elevation` | `ResponsiveProp` | No | `-` | - |
| `flexBasis` | `ResponsiveProp>` | No | `-` | - |
| `flexDirection` | `ResponsiveProp` | No | `-` | - |
| `flexGrow` | `ResponsiveProp` | No | `-` | - |
| `flexShrink` | `ResponsiveProp` | No | `-` | - |
| `flexWrap` | `ResponsiveProp` | No | `-` | - |
| `font` | `ResponsiveProp` | No | `-` | - |
| `fontFamily` | `ResponsiveProp` | No | `-` | - |
| `fontSize` | `ResponsiveProp` | No | `-` | - |
| `fontWeight` | `ResponsiveProp` | No | `-` | - |
| `gap` | `ResponsiveProp` | No | `-` | - |
| `grid` | `ResponsiveProp` | No | `-` | - |
| `gridArea` | `ResponsiveProp` | No | `-` | - |
| `gridAutoColumns` | `ResponsiveProp>` | No | `-` | - |
| `gridAutoFlow` | `ResponsiveProp` | No | `-` | - |
| `gridAutoRows` | `ResponsiveProp>` | No | `-` | - |
| `gridColumn` | `ResponsiveProp` | No | `-` | - |
| `gridColumnEnd` | `ResponsiveProp` | No | `-` | - |
| `gridColumnStart` | `ResponsiveProp` | No | `-` | - |
| `gridRow` | `ResponsiveProp` | No | `-` | - |
| `gridRowEnd` | `ResponsiveProp` | No | `-` | - |
| `gridRowStart` | `ResponsiveProp` | No | `-` | - |
| `gridTemplate` | `ResponsiveProp` | No | `-` | - |
| `gridTemplateAreas` | `ResponsiveProp` | No | `-` | - |
| `gridTemplateColumns` | `ResponsiveProp>` | No | `-` | - |
| `gridTemplateRows` | `ResponsiveProp>` | No | `-` | - |
| `height` | `ResponsiveProp>` | No | `-` | - |
| `justifyContent` | `ResponsiveProp` | No | `-` | - |
| `key` | `Key \| null` | No | `-` | - |
| `left` | `ResponsiveProp>` | No | `-` | - |
| `lineHeight` | `ResponsiveProp` | No | `-` | - |
| `margin` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
| `marginBottom` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
| `marginEnd` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
| `marginStart` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
| `marginTop` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
| `marginX` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
| `marginY` | `ResponsiveProp<0 \| -5 \| -10 \| -0.25 \| -0.5 \| -0.75 \| -1 \| -1.5 \| -2 \| -3 \| -4 \| -6 \| -7 \| -8 \| -9>` | No | `-` | - |
| `maxHeight` | `ResponsiveProp>` | No | `-` | - |
| `maxWidth` | `ResponsiveProp>` | No | `-` | - |
| `minHeight` | `ResponsiveProp>` | No | `-` | - |
| `minWidth` | `ResponsiveProp>` | No | `-` | - |
| `onChange` | `FormEventHandler` | No | `-` | - |
| `opacity` | `ResponsiveProp` | No | `-` | - |
| `overflow` | `ResponsiveProp` | No | `-` | - |
| `padding` | `ResponsiveProp` | No | `-` | - |
| `paddingBottom` | `ResponsiveProp` | No | `-` | - |
| `paddingEnd` | `ResponsiveProp` | No | `-` | - |
| `paddingStart` | `ResponsiveProp` | No | `-` | - |
| `paddingTop` | `ResponsiveProp` | No | `-` | - |
| `paddingX` | `ResponsiveProp` | No | `-` | - |
| `paddingY` | `ResponsiveProp` | No | `-` | - |
| `pin` | `PinningDirection` | No | `-` | Direction in which to absolutely pin the box. |
| `position` | `ResponsiveProp` | No | `-` | - |
| `ref` | `RefObject \| ((instance: HTMLDivElement \| null) => void) \| null` | No | `-` | - |
| `right` | `ResponsiveProp>` | No | `-` | - |
| `rowGap` | `ResponsiveProp` | No | `-` | - |
| `secondaryAction` | `ReactElement & { variant?: ButtonVariant; disabled?: boolean \| undefined; loading?: boolean \| undefined; progressCircleSize?: number \| undefined; transparent?: boolean \| undefined; block?: boolean \| undefined; compact?: boolean \| undefined; children: ReactNode; start?: ReactNode; startIcon?: IconName \| undefined; startIconActive?: boolean \| undefined; end?: ReactNode; endIcon?: IconName \| undefined; endIconActive?: boolean \| undefined; flush?: start \| end \| undefined; name?: string \| undefined; noScaleOnPress?: boolean \| undefined; numberOfLines?: number \| undefined; } & Omit & { onClick?: MouseEventHandler \| undefined; }, string \| JSXElementConstructor> \| undefined` | No | `-` | Secondary action button |
| `style` | `CSSProperties` | No | `-` | - |
| `testID` | `string` | No | `-` | Used to locate this element in unit and end-to-end tests. Under the hood, testID translates to data-testid on Web. On Mobile, testID stays the same - testID |
| `textAlign` | `ResponsiveProp` | No | `-` | - |
| `textDecoration` | `ResponsiveProp` | No | `-` | - |
| `textTransform` | `ResponsiveProp` | No | `-` | - |
| `top` | `ResponsiveProp>` | No | `-` | - |
| `transform` | `ResponsiveProp` | No | `-` | - |
| `userSelect` | `ResponsiveProp` | No | `-` | - |
| `visibility` | `ResponsiveProp` | No | `-` | - |
| `width` | `ResponsiveProp>` | No | `-` | - |
| `zIndex` | `ResponsiveProp` | No | `-` | - |