Skip to contentSkip to navigationSkip to topbar
Paste assistant Assistant
Figma
Star

Form Pill Group

Version 8.0.1GithubStorybook

A Form Pill Group is an editable set of Pills that represent a collection of selectable or removable objects.

Component preview theme
const BasicFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState}>
Voice
</FormPill>
<FormPill {...pillState}>
<ProductVideoIcon decorative />
Video
</FormPill>
<FormPill {...pillState}>
<ProductVerifyIcon decorative />
Verify
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<BasicFormPillGroup />
)

Guidelines

Guidelines page anchor

About Form Pill Group

About Form Pill Group page anchor

A Form Pill Group is an editable set of Pills that represents a collection of selectable and/or removable objects. They are used almost exclusively in multi-select editing situations.

A Form Pill Group can be used on its own to represent selection across a number of fields, such as showing currently applied filters. It can also be paired directly with an input field, such as a Combobox, to represent multiple selections.

Accessibility

Accessibility page anchor

The only accessibility requirement is providing the Pill Group a descriptive label via the aria-label React prop.

Keyboard navigation
Keyboard navigation page anchor

The Form Pill Group is focusable, but only one pill is focusable at a time. This means the Pill Group is a single tab stop to a keyboard user. The dismiss button in a Form Pill is not a focusable element, but can be clickable.

Once a user focuses within the Form Pill Group, they can use these keyboard interactions:

Keyboard interactionAction
Left and right arrow keysMoving focus within the group
Enter keyTriggers the supplied action via onClick
Spacebar or enter keysSelects a pill
Delete or backspace keysDismisses a pill

Form Pill vs. Display Pill

Form Pill vs. Display Pill page anchor

Form Pill Group creates a list from which a user may select items, whereas a Display Pill Group creates a list of static items.

Use the table below to get a better understanding of when to use Form Pill or Display Pill.

FunctionalityDisplay PillForm Pill
Used to edit data in a form 
Supported feature
Uses Listbox, Option semantic 
Supported feature
Provides advanced keyboard navigation 
Supported feature
Pills can be selected 
Supported feature
Pills can perform an action 
Supported feature
Pills can be dismissed 
Supported feature
Used to view data
Supported feature
 
Uses List, List Item semantic
Supported feature
 
Pills can link to a page
Supported feature
 

This Form Pill example shows the basic static component that's exported by Paste. A Form Pill can have an optional Avatar or Icon placed before the text.

Interaction states on Form Pills need to be managed separately as shown in the examples after this one by using the useFormPillState hook. The returned state should be spread onto each component. This will provide you with some internal state management and keyboard navigation.

A Form Pill can have an optional Avatar or Icon placed before the text.

Component preview theme
const BasicFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState}>
Voice
</FormPill>
<FormPill {...pillState}>
<ProductVideoIcon decorative />
Video
</FormPill>
<FormPill {...pillState}>
<ProductVerifyIcon decorative />
Verify
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<BasicFormPillGroup />
)

Use size="large" Form Pills only for specific and approved use cases, such as in the filter group pattern (link coming soon!).

Component preview theme
const BasicFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:" size="large">
<FormPill {...pillState}>
Voice
</FormPill>
<FormPill {...pillState}>
<ProductVideoIcon decorative />
Video
</FormPill>
<FormPill {...pillState}>
<ProductVerifyIcon decorative />
Verify
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<BasicFormPillGroup />
)

Use a Selectable Form Pill to show an option that a user can select or deselect.

To make pills inside the Pill Group selectable, you can manage the selection state yourself and combine it with the state returned from the useFormPillState hook. To do so, track which pill is selected in a separate store of state. When rendering the Form Pill Group, cross reference the rendered pills with the selection state, and conditionally set selected on each FormPill that requires it.

The onSelect event handler will fire whenever the pill is clicked, or the spacebar or enter key is pressed. Use this to respond to your users' selection interactions.

Component preview theme
const SelectableFormPillGroup = () => {
const [pills] = React.useState(['SMS', 'MMS', 'Fax', 'Voice', 'Messaging', 'Chat']);
const [selectedSet, updateSelectedSet] = React.useState(new Set(['MMS', 'Voice', 'Chat']));
const pillState = useFormPillState();
const iconMap = {
['Fax']: <FaxCapableIcon decorative/>,
['Voice']: <ProductVoiceIcon decorative/>,
['Messaging']: <ProductMessagingIcon decorative/>,
['Chat']: <ProductChatIcon decorative/>,
}
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
{pills.map((pill) => (
<FormPill
key={pill}
{...pillState}
selected={selectedSet.has(pill)}
onSelect={() => {
const newSelectedSet = new Set(selectedSet);
if (newSelectedSet.has(pill)) {
newSelectedSet.delete(pill);
} else {
newSelectedSet.add(pill);
}
updateSelectedSet(newSelectedSet);
}}
>
{iconMap[pill]}
{pill}
</FormPill>
))}
</FormPillGroup>
</form>
);
};
render(
<SelectableFormPillGroup />
)

Use a Dismissible Form Pill to show an option that a user can remove from the group. Once the pill is dismissed, it can’t be rerendered.

Form Pills are given a close button when provided an onDismiss event handler. Because the Form Pill Group is largely presentational, provide dismissible functionality by managing the state of the rendered pills. By responding to user interactions and hooking into the onDismiss event handler, the rendered state of the Form Pill Group can be updated and pills can be selectively removed from the collection.

The onDismiss event handler will fire when a user clicks on the close button, or presses their backspace or delete key when focused on a pill.

Component preview theme
const DismissableFormPillGroup = () => {
const [pills, setPills] = React.useState(['Frontline', 'Phone Numbers', 'Authy']);
const pillState = useFormPillState();
const iconMap = {
['Phone Numbers']: <ProductPhoneNumbersIcon decorative/>,
}
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
{pills.map((pill, index) => (
<FormPill
key={pill}
{...pillState}
onDismiss={() => {
setPills(pills.filter((_, i) => i !== index));
}}
>
{iconMap[pill]}
{pill}
</FormPill>
))}
</FormPillGroup>
</form>
);
};
render(
<DismissableFormPillGroup />
)

Selectable and dismissible

Selectable and dismissible page anchor

Use a Selectable and Dismissible Form Pill to show an option that a user can select, deselect, or dismiss. Once the pill is dismissed, it can’t be re-rendered.

The onSelect event handler will fire when a user clicks on the pill. The onDismiss event handler will fire when a user clicks on the close button.

The onSelect event handler will fire when a user presses the spacebar or enter keys. The onDismiss event handler will fire when a user presses the delete or backspace keys.

Component preview theme
const SelectableAndDismissableFormPillGroup = () => {
const [pills, setPills] = React.useState(['Proxy', 'Interconnect', 'Trust Hub']);
const [selectedSet, updateSelectedSet] = React.useState(new Set(['Interconnect']));
const pillState = useFormPillState();
const iconMap = {
['Interconnect']: <ProductInterconnectIcon decorative/>,
}
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
{pills.map((pill, index) => (
<FormPill
key={pill}
{...pillState}
onDismiss={() => {
setPills(pills.filter((_, i) => i !== index));
if (selectedSet.has(pill)) {
const newSelectedSet = new Set(selectedSet);
newSelectedSet.delete(pill);
updateSelectedSet(newSelectedSet);
}
}}
selected={selectedSet.has(pill)}
onSelect={() => {
const newSelectedSet = new Set(selectedSet);
if (newSelectedSet.has(pill)) {
newSelectedSet.delete(pill);
} else {
newSelectedSet.add(pill);
}
updateSelectedSet(newSelectedSet);
}}
>
{iconMap[pill]}
{pill}
</FormPill>
))}
</FormPillGroup>
</form>
);
};
render(
<SelectableAndDismissableFormPillGroup />
)

To internationalize the form pill group, simply pass different text as children to the pills. The only exceptions to this are the visually hidden text that explains how to dismiss and select pills and the error label for the error variant. To change these, pass the i18nKeyboardControls and i18nErrorLabel props.

Component preview theme
const I18nFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup
{...pillState}
aria-label="Votre sports favoris:"
i18nKeyboardControls="Appuyez sur Supprimer ou Retour arrière pour supprimer. Appuyez sur Entrée pour basculer la sélection."
>
<FormPill {...pillState} variant="error" i18nErrorLabel="(Erreur)">
Le tennis
</FormPill>
<FormPill {...pillState}>
Le football américain
</FormPill>
<FormPill {...pillState} variant="error" i18nErrorLabel="(Erreur)">
Le ski
</FormPill>
<FormPill {...pillState}>Le football</FormPill>
</FormPillGroup>
</form>
);
};
render(
<I18nFormPillGroup />
)

The default state of a Form Pill indicates that the control is static or not selected.

Component preview theme
const BasicFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState}>
Voice
</FormPill>
<FormPill {...pillState}>
<ProductVideoIcon decorative />
Video
</FormPill>
<FormPill {...pillState}>
<ProductVerifyIcon decorative />
Verify
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<BasicFormPillGroup />
)

A Form Pill can be placed into a selected state by setting the selected property.

Component preview theme
const SelectedStateExample = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState} selected>
Voice
</FormPill>
<FormPill {...pillState} selected>
<ProductVideoIcon decorative />
Video
</FormPill>
<FormPill {...pillState} selected>
<ProductVerifyIcon decorative />
Verify
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<SelectedStateExample />
)

Use an Error Form Pill to highlight an object that the user must be made aware of because it’s considered to be in a bad or broken state and should be addressed. An error icon will display as a prefix to the rest of the children in the pill.

When used in a form field, display an error message below the form field to provide guidance on how to fix the error. For additional guidance on how to compose error messages, refer to the error state pattern.

Component preview theme
const ErrorStateExample = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState} variant="error">
Voice
</FormPill>
<FormPill {...pillState} variant="error" selected>
<ProductVideoIcon decorative />
Video
</FormPill>
<FormPill {...pillState} variant="error">
<ProductVerifyIcon decorative />
Verify
</FormPill>
<FormPill {...pillState} variant="error" selected>
Usage
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<ErrorStateExample />
)

Use a disabled Form Pill to indicate that a particular option cannot be interacted with or can be safely ignored.

Component preview theme
const DisabledStateExample = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState} disabled>
Voice
</FormPill>
<FormPill {...pillState} disabled>
<ProductVideoIcon decorative />
Video
</FormPill>
<FormPill {...pillState} disabled>
<ProductVerifyIcon decorative />
Verify
</FormPill>
<FormPill {...pillState} disabled>
Usage
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<DisabledStateExample />
)

Pill text should never wrap to the next line. If the text length is longer than the max width of the pill group’s container, consider moving the Pill to a new row or use Truncate to truncate the Form Pill text.

Component preview theme
const TruncateFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState}>
<ProductInternetOfThingsIcon decorative />
<Box maxWidth="size10">
<Truncate title="Internet of Things">Internet of Things</Truncate>
</Box>
</FormPill>
<FormPill {...pillState}>
<ProductMarketingCampaignsIcon decorative />
<Box maxWidth="size10">
<Truncate title="Marketing Campaigns">Marketing Campaigns</Truncate>
</Box>
</FormPill>
<FormPill {...pillState}>
<ProductCodeExchangePartnerIcon decorative />
<Box maxWidth="size10">
<Truncate title="CodeExchange Partner">CodeExchange Partner</Truncate>
</Box>
</FormPill>
<FormPill {...pillState}>
<ProductEngagementIntelligencePlatformIcon decorative />
<Box maxWidth="size10">
<Truncate title="Engagement Intelligence Platform">Engagement Intelligence Platform</Truncate>
</Box>
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<TruncateFormPillGroup />
)

A Form Pill can have an optional Avatar. Considering the size of a Form Pill, it is recommended to use either an image or icon within an Avatar, and to avoid using initials as some initials may get cut off if the characters are particularly wide.

We recommend placing the Avatar ahead of the pill text. Use no more than one before or after the text.

Component preview theme
const AvatarFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="People:">
<FormPill {...pillState}>
<Avatar size='sizeIcon30' name='Customer' src="/images/avatars/avatar4.png" />
Customer
</FormPill>
<FormPill {...pillState}>
<Avatar size='sizeIcon30' name='Agent' icon={AgentIcon} />
Agent
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<AvatarFormPillGroup />
)

A Form Pill can have an optional Icon. We recommend placing the Icon ahead of the pill text. Use no more than one before or after the text.

Component preview theme
const IconFormPillGroup = () => {
const pillState = useFormPillState();
return (
<form>
<FormPillGroup {...pillState} aria-label="Products:">
<FormPill {...pillState}>
<ProductMessagingIcon decorative />
Messaging
</FormPill>
<FormPill {...pillState}>
<ProductBillingIcon decorative />
Billing
</FormPill>
<FormPill {...pillState}>
<ProductLookupIcon decorative />
Lookup
</FormPill>
<FormPill {...pillState}>
<ProductConversationsIcon decorative />
Conversations
</FormPill>
</FormPillGroup>
</form>
);
};
render(
<IconFormPillGroup />
)

When to use a Form Pill Group

When to use a Form Pill Group page anchor

Use a Form Pill Group when you’re editing a collection of data within a form. It can be used to represent selection across multiple fields such as filtering, or from a single field like a Combobox.

Do

Use Form Pills inside of a form or when editing a collection of data.

Don't

Don’t use Form Pills in non-editable pages to represent a collection of similar objects, outside of a form, or outside of editing scenarios. Use a Display Pill Group instead.

Do

Only pass FormPill as a direct descendent of a FormPillGroup.

Don't

Don't pass FormPillGroup any other component type, and do not wrap FormPill in any other component of DOM element.