Skip to main content
In progress

BottomSheet

BottomSheet displays content in a panel that slides up from the bottom of the viewport. It is optimized for mobile and touch-based interactions and can be either modal or non-modal. BottomSheet handles gestures, positioning, overlay behavior, and accessibility, and often contains a Dialog component for structured content.

BottomSheet Title

BottomSheet content goes here

Code example
import { BottomSheet, useBottomSheet, Button, Dialog } from '@yleisradio/yds-components-react';

const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<>
<Button onClick={openBottomSheet}>Open Bottom Sheet</Button>
<BottomSheet bottomSheetRef={bottomSheetRef}>
<Dialog
title="BottomSheet Title"
actions={
<>
<Button text="OK" />
</>
}
>
<p>BottomSheet content goes here</p>
</Dialog>
</BottomSheet>
</>

Why to use

Use BottomSheet to present contextual content, actions, or settings while preserving the user’s sense of place. It provides a mobile-friendly alternative to Modal with natural touch interactions, flexible sizing, and optional non-modal behavior. BottomSheet standardizes mobile dialog behavior across YDS and ensures accessible gesture and keyboard handling.

When to use

Use BottomSheet when content is closely related to the current view and especially in mobile or touch-first contexts.

Do
  • Use BottomSheet for mobile dialogs, filters, and settings.
  • Use it when content should feel lightweight and dismissible.
  • Combine BottomSheet with Dialog for structured content.
Don't
  • Don’t use BottomSheet for critical confirmations that require full attention.
  • Don’t use BottomSheet for complex, multi-step workflows.
  • Don’t replace desktop modals with BottomSheet unnecessarily.

Content Guidelines

Use these guidelines when placing content inside a BottomSheet.

Do
  • Keep content task-focused and lightweight.
  • Prefer short forms and clear actions.
  • Use clear completion actions such as “Done” or “Apply”.
Don't
  • Don’t overload the sheet with long scrolling content unless using the expanded size.
  • Don’t hide critical actions below excessive content.
  • Don’t rely on drag-to-close as the only way to dismiss the sheet.

Anatomy

BottomSheet anatomy

  1. Drag handle / close affordance – Visual indicator that the sheet can be dragged or dismissed.
  2. Sheet container – The surface that slides up from the bottom and contains all sheet content.
  3. Bottom Sheet title – Required heading that describes the purpose of the sheet.
  4. Content area – The main content section for text or interactive elements. Supports internal scrolling when content exceeds available height.
  5. Actions area (optional) – Footer area for primary and secondary actions, aligned consistently across devices.

Key BottomSheet Props

Use these props to configure the BottomSheet component.

bottomSheetRef

Ref handle for controlling the bottom sheet (open/close). Use with useBottomSheet hook.

TypeExampleDescription
React.RefObject<BottomSheetHandle>

Bottom Sheet

Content here
Ref handle for programmatic control
Code example
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet bottomSheetRef={bottomSheetRef}>
<div>Content here</div>
<Button onClick={closeBottomSheet}>Done</Button>
</BottomSheet>

title

Optional heading text displayed in the sheet header.

TypeExampleDescription
string

Settings

Content here
Title text for the bottom sheet
Code example
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet title="Settings" bottomSheetRef={bottomSheetRef}>
<div>Content here</div>
<Button onClick={closeBottomSheet}>Done</Button>
</BottomSheet>

titleAs

HTML tag name to render for the title.

TypeExampleDescription
string

Bottom Sheet

Content here
HTML element for title (e.g., 'h2')
Code example
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet titleAs="h3" bottomSheetRef={bottomSheetRef}>
<div>Content here</div>
<Button onClick={closeBottomSheet}>Done</Button>
</BottomSheet>

dragToClose

Enables pointer drag gesture and handle for closing the sheet.

ValueExampleDescription
true

Bottom Sheet

Content here
Enables drag-to-close functionality
false

Bottom Sheet

Content here
Disables drag-to-close functionality
Code example
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet dragToClose bottomSheetRef={bottomSheetRef}>
<div>Content here</div>
<Button onClick={closeBottomSheet}>Done</Button>
</BottomSheet>

align

Horizontal alignment of the sheet.

ValueExampleDescription
center

Bottom Sheet

Content here
Centers the sheet horizontally
left

Bottom Sheet

Content here
Aligns sheet to the left
right

Bottom Sheet

Content here
Aligns sheet to the right
Code example
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet align="center" bottomSheetRef={bottomSheetRef}>
<div>Content here</div>
<Button onClick={closeBottomSheet}>Done</Button>
</BottomSheet>

nonModal

If true, renders a non-modal dialog (no backdrop, background remains interactive).

ValueExampleDescription
true

Bottom Sheet

Content here
Makes the sheet non-modal
false

Bottom Sheet

Content here
Makes the sheet modal
Code example
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet nonModal={true} bottomSheetRef={bottomSheetRef}>
<div>Content here</div>
<Button onClick={closeBottomSheet}>Done</Button>
</BottomSheet>

closeOnOutsideClick

If true, clicking outside the sheet closes it (modal only).

ValueExampleDescription
true

Bottom Sheet

Content here
Controls outside click closing behavior
false

Bottom Sheet

Content here
Controls outside click closing behavior
Code example
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet closeOnOutsideClick={true} bottomSheetRef={bottomSheetRef}>
<div>Content here</div>
<Button onClick={closeBottomSheet}>Done</Button>
</BottomSheet>

size

Size variant of the bottom sheet.

ValueExampleDescription
compact

Bottom Sheet

What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it? It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). Where does it come from? Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. Where can I get some? There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
Compact size variant
default

Bottom Sheet

What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it? It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). Where does it come from? Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. Where can I get some? There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
Default size variant
expanded

Bottom Sheet

What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it? It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). Where does it come from? Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. Where can I get some? There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
Expanded size variant
Code example
<BottomSheet size="compact" bottomSheetRef={bottomSheetRef}>
<Dialog title="Content">Content</Dialog>
</BottomSheet>

Behavior

  • BottomSheet slides in from the bottom of the viewport and animates smoothly when opening and closing.
  • The sheet opens when openBottomSheet() is called or when the ref’s open() method is invoked.
  • The sheet closes when closeBottomSheet() is called, the close button or drag handle is activated, the Escape key is pressed, or the user drags the sheet down past the close threshold.
  • Drag-to-close interaction is enabled when dragToClose={true}. If the drag distance exceeds the threshold (100px), the sheet closes; otherwise, it snaps back into place.
  • Modal bottom sheets (default) block background interaction and disable body scrolling.
  • Non-modal bottom sheets (nonModal={true}) allow background interaction and do not disable body scrolling.
  • Focus and keyboard behavior adapt based on modality.
  • The Escape key always closes modal bottom sheets and closes non-modal sheets only when focus is inside the sheet.
  • Clicking outside the sheet closes it only when closeOnOutsideClick={true} and the sheet is modal.
  • The onClose callback fires after the sheet has fully closed (native dialog close event).
  • Size variants (compact, default, expanded) control how much vertical space the sheet initially occupies.

Accessibility

  • BottomSheet uses the native HTML <dialog> element to ensure correct semantics and screen reader support (WCAG 4.1.2 — Name, Role, Value).
  • Modal BottomSheets trap keyboard focus while open and prevent interaction with background content, whereas non-modal BottomSheets allow background interaction and only handle keyboard events when focus is inside the sheet.
  • The sheet can always be closed using the Escape key (WCAG 2.1.1 — Keyboard).
  • Drag-to-close is a supplementary interaction; keyboard and button-based closing must always be available.

Implementation Examples

Game instructions BottomSheet with Dialog

Peliohjeet

Pelissä tavoitteena on löytää neljä sanaryhmää, jotka muodostuvat kahdesta, kolmesta, neljästä tai viidestä sanasta. Jokaisen ryhmän sanoja yhdistää jokin yhteinen tekijä, kuten merkitys, käyttötarkoitus tai muu selkeä ominaisuus. Lisäksi pyramidin huipulle jää yksi sana, joka ei kuulu yhteenkään ryhmään. Tämä sana toimii ikään kuin ylimääräisenä haasteena ja auttaa varmistamaan, että ryhmittely on tehty oikein.

Esimerkkejä mahdollisista ryhmistä ovat MATTI, TOIVO, YRJÖ, ERKKI, jotka ovat miesten nimiä, tai TIE, KATU, KUJA, jotka kaikki liittyvät kulkuväyliin. Ryhmät voivat vaihdella kooltaan, ja niiden löytäminen vaatii tarkkaavaisuutta sekä sanojen merkitysten pohtimista eri näkökulmista.Valitse sanat klikkaamalla niitä. Kun olet valinnut sanat, jotka mielestäsi kuuluvat samaan ryhmään eli samaan pyramidin kerrokseen, voit tarkistaa valintasi painamalla Tarkista. Jos haluat perua jonkin valinnan, klikkaa sanaa uudelleen, jolloin se poistuu valinnoista. Kun valittu rivi on oikein, se lukittuu paikalleen pyramidissa eikä sitä voi enää muuttaa.

Jos peli tuntuu etenevän hankalasti, voit käyttää apuna Vihje-toimintoa. Vihjettä klikkaamalla saat näkyviin ensimmäisen sanan alimmasta vielä ratkaisemattomasta rivistä. Vihje on käytettävissä vain kolmen, neljän tai viiden sanan riveillä. Huomioithan, että vihjeenä saatu sana ei tule automaattisesti valituksi, vaan se täytyy klikata mukaan muiden sanojen tapaan.Ryhmien ratkaiseminen ei vaadi tiettyä järjestystä, vaan voit edetä vapaasti ja ratkaista helpoimmilta tuntuvat ryhmät ensin. Tämä antaa mahdollisuuden kokeilla erilaisia lähestymistapoja ja tarkentaa omaa ajattelua pelin edetessä.

Peli sallii rajallisen määrän virheitä. Neljän väärän vastauksen jälkeen peli päättyy, joten jokainen tarkistus kannattaa tehdä harkiten. Tarkka lukeminen, sanojen merkitysten pohtiminen ja rauhallinen eteneminen auttavat pääsemään parhaaseen lopputulokseen.

Code example
import { Button, useBottomSheet, BottomSheet, Dialog } from '@yleisradio/yds-components-react';

export const BottomSheetGameInstructions = () => {
const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

return (
<>
<BottomSheet bottomSheetRef={bottomSheetRef}>
<Dialog
title="Peliohjeet"
actions={
<>
<Button text="Sulje" onClick={closeBottomSheet} />
</>
}
>
<p>
Pelissä tavoitteena on löytää neljä sanaryhmää, jotka muodostuvat kahdesta, kolmesta,
neljästä tai viidestä sanasta. Jokaisen ryhmän sanoja yhdistää jokin yhteinen tekijä,
kuten merkitys, käyttötarkoitus tai muu selkeä ominaisuus. Lisäksi pyramidin huipulle
jää yksi sana, joka ei kuulu yhteenkään ryhmään. Tämä sana toimii ikään kuin
ylimääräisenä haasteena ja auttaa varmistamaan, että ryhmittely on tehty oikein.
</p>
<p>
Esimerkkejä mahdollisista ryhmistä ovat MATTI, TOIVO, YRJÖ, ERKKI, jotka ovat miesten
nimiä, tai TIE, KATU, KUJA, jotka kaikki liittyvät kulkuväyliin. Ryhmät voivat vaihdella
kooltaan, ja niiden löytäminen vaatii tarkkaavaisuutta sekä sanojen merkitysten
pohtimista eri näkökulmista.Valitse sanat klikkaamalla niitä. Kun olet valinnut sanat,
jotka mielestäsi kuuluvat samaan ryhmään eli samaan pyramidin kerrokseen, voit tarkistaa
valintasi painamalla Tarkista. Jos haluat perua jonkin valinnan, klikkaa sanaa
uudelleen, jolloin se poistuu valinnoista. Kun valittu rivi on oikein, se lukittuu
paikalleen pyramidissa eikä sitä voi enää muuttaa.
</p>
<p>
Jos peli tuntuu etenevän hankalasti, voit käyttää apuna Vihje-toimintoa. Vihjettä
klikkaamalla saat näkyviin ensimmäisen sanan alimmasta vielä ratkaisemattomasta rivistä.
Vihje on käytettävissä vain kolmen, neljän tai viiden sanan riveillä. Huomioithan, että
vihjeenä saatu sana ei tule automaattisesti valituksi, vaan se täytyy klikata mukaan
muiden sanojen tapaan.Ryhmien ratkaiseminen ei vaadi tiettyä järjestystä, vaan voit
edetä vapaasti ja ratkaista helpoimmilta tuntuvat ryhmät ensin. Tämä antaa
mahdollisuuden kokeilla erilaisia lähestymistapoja ja tarkentaa omaa ajattelua pelin
edetessä.
</p>
<p>
Peli sallii rajallisen määrän virheitä. Neljän väärän vastauksen jälkeen peli päättyy,
joten jokainen tarkistus kannattaa tehdä harkiten. Tarkka lukeminen, sanojen merkitysten
pohtiminen ja rauhallinen eteneminen auttavat pääsemään parhaaseen lopputulokseen.
</p>
</Dialog>
</BottomSheet>
<Button onClick={openBottomSheet}>Open BottomSheet</Button>
</>
);
};

Nickname BottomSheet with custom content

Aseta itsellesi nimimerkki

Nimimerkin pitää olla asiallinen ja noudattaa hyvää makua.
Code example
import { BottomSheet, useBottomSheet, Button, TextInput } from '@yleisradio/yds-components-react';

const { bottomSheetRef, openBottomSheet, closeBottomSheet } = useBottomSheet();

<BottomSheet bottomSheetRef={bottomSheetRef} title="Aseta itsellesi nimimerkki">
<div>Nimimerkin pitää olla asiallinen ja noudattaa hyvää makua.</div>
<TextInput label="Nimimerkki" placeholder="Aseta nimimerkki" />
<Button onClick={closeBottomSheet}>Hyväksy</Button>
</BottomSheet>

API Reference

Props

The BottomSheet component accepts all standard HTML <dialog> attributes in addition to the following props:

PropTypeRequiredDefaultDescription
bottomSheetRefReact.RefObject<BottomSheetHandle>NoRef handle for controlling the bottom sheet
titlestringNoOptional heading text displayed in header
titleAsstringNoHTML tag name to render for title (e.g., 'h2')
onClose() => voidNoCallback fired after sheet closes
dragToClosebooleanNotrueEnables drag-to-close functionality
align'center' | 'left' | 'right'No'center'Horizontal alignment of the sheet
nonModalbooleanNofalseRenders as non-modal dialog
closeOnOutsideClickbooleanNotrueCloses sheet on outside click (modal only)
size'compact' | 'default' | 'expanded'No'default'Size variant of the bottom sheet
childrenReact.ReactNodeNoContent displayed inside the sheet

Type Definitions

export interface BottomSheetDSProps {
bottomSheetRef?: React.RefObject<BottomSheetHandle>;
title?: string;
titleAs?: string;
onClose?: () => void;
children?: React.ReactNode;
dragToClose?: boolean;
align?: 'center' | 'left' | 'right';
nonModal?: boolean;
closeOnOutsideClick?: boolean;
size?: 'compact' | 'default' | 'expanded';
}

export type BottomSheetProps = HTMLAttributes<HTMLDialogElement> & BottomSheetDSProps;

export type BottomSheetHandle = {
open: () => void;
close: () => void;
};

useBottomSheet Hook

The useBottomSheet hook provides a convenient way to manage bottom sheet state.

Returns

PropertyTypeDescription
bottomSheetRefReact.RefObject<BottomSheetHandle>Ref object to pass to the BottomSheet component
openBottomSheet() => voidFunction to open the bottom sheet
closeBottomSheet() => voidFunction to close the bottom sheet
  • Modal – Alternative modal component.
  • Dialog – Often used inside BottomSheet for content structure.
  • Accordion – Can be used inside a BottomSheet to organise collapsible content.