Spoiler
Install: npm install @mantine/core@9.7.1 @mantine/hooks@9.7.1
Version and compatibility
Pinned Mantine Core 9.7.1 requires React and React DOM ^19.2.0 and matching @mantine/hooks@9.7.1, from the installed npm package's peerDependencies. Do not force it into React 18 or another incompatible version. Explain upgrade impact and agree on a plan with the project owner; do not hide conflicts with --force/--legacy-peer-deps. The native Provider enables theme.respectReducedMotion and uses forceColorScheme for light/dark.
Use case
Expand content beyond a collapsed maximum height.
Native props used by this example
These props are extracted from the local typechecked JSX and grouped by their owning native component. This is not an exhaustive prop list; see pinned source signatures and inheritance below.
| Component prop | Explanation |
|---|---|
Spoiler.maxHeight | Collapsed content height boundary. |
Spoiler.showLabel | Expand-action label. |
Spoiler.hideLabel | Collapse-action label. |
Interaction and state checks
Current scenarios: Default. Check actual behavior after selecting supported disabled/error/read-only scenarios; components without those native states do not receive invented universal props. Verify keyboard focus and exits, then connect changed values through the native callback signature.
Business integration boundary
Demo content/state is local, without server persistence, authorization or fetching. A static display component is not a completed business workflow.
Host setup
Install pinned @mantine/core and @mantine/hooks, import @mantine/core/styles.css at the entry and use MantineProvider. The example's theme/variant are JGuang-authored host props, not universal native component APIs; forceColorScheme synchronizes the app theme. Native implementation source is a reading reference: use npm exports rather than copying internal engines. This batch covers Core, not dates/charts/forms extension packages. Detailed upstream prose remains English.
Runnable usage
import { useState } from 'react';
import * as M from '@mantine/core';
import '@mantine/core/styles.css';
function ComponentPreview({ variant = 'Default' }: { variant?: string }) {
return <M.Spoiler maxHeight={60} showLabel="Show more" hideLabel="Hide">{Array.from({ length: 6 }, (_, i) => <M.Text key={i}>Design resources for your next project.</M.Text>)}</M.Spoiler>;
}
export default function Example({ variant = 'Default', theme = 'light' }: { variant?: string; theme?: 'light' | 'dark' }) {
return <M.MantineProvider forceColorScheme={theme} theme={{ respectReducedMotion: true }}><ComponentPreview variant={variant} /></M.MantineProvider>;
}
Preview states
Default
The example uses the native Mantine API. For supported input demos, change the local variant to Disabled, Invalid or Read only; these are example labels, not new component props. Keep the documented data, controlled values, stores and compound children when adapting the example.
Usage
Use Spoiler to hide a long section of content.
Set the maxHeight prop to control the point at which content will be hidden under the spoiler and the show/hide control appears.
If the content height is less than maxHeight, the spoiler will just render children.
hideLabel and showLabel props are required – they are used as the spoiler toggle button label in the corresponding state.
Control expanded state
To control the expanded state, use expanded and onExpandedChange props. Note that the
expanded prop does not have any effect on spoiler visuals if the content height
is less than the given maxHeight.
import { useState } from 'react';
import { Spoiler } from '@mantine/core';
function Demo() {
const [expanded, setExpanded] = useState(false);
return (
<Spoiler
showLabel="Show more"
hideLabel="Hide details"
expanded={expanded}
onExpandedChange={setExpanded}
>
{/* Spoiler content */}
</Spoiler>
);
}
Subscribe to expanded state changes
Use onExpandedChange to subscribe to expanded state changes:
import { Spoiler } from '@mantine/core';
function Demo() {
return (
<Spoiler
showLabel="Show more"
hideLabel="Hide details"
onExpandedChange={(expanded) => console.log(expanded)}
>
{/* Spoiler content */}
</Spoiler>
);
}
Transition duration
Control the transition duration by setting the transitionDuration prop (transition-duration CSS property in ms).
To disable animations, set transitionDuration={0}:
Get control ref
import { useRef } from 'react';
import { Spoiler } from '@mantine/core';
function Demo() {
const spoilerControlRef = useRef<HTMLButtonElement>(null);
return (
<Spoiler
controlRef={spoilerControlRef}
hideLabel="Hide"
showLabel="Show"
/>
);
}
Accessibility
The Spoiler component implements proper ARIA attributes for screen reader support:
- Toggle button has
aria-expandedindicating the expanded/collapsed state - Content region has
role="region"and is associated with the button viaaria-controls - Keyboard support: Space or Enter key toggles the spoiler when the button is focused
Best practices for label text
Provide descriptive labels for showLabel and hideLabel props that clearly indicate the action:
// Good - clear, descriptive labels
<Spoiler showLabel="Show full article" hideLabel="Hide article" />
<Spoiler showLabel="Expand details" hideLabel="Collapse details" />
// Avoid vague labels
<Spoiler showLabel="More" hideLabel="Less" />
<Spoiler showLabel="..." hideLabel="..." />
Custom accessibility labels
If your button labels don't clearly describe the action for screen reader users, use the showAriaLabel and hideAriaLabel props to provide custom ARIA labels:
import { Spoiler } from '@mantine/core';
function Demo() {
return (
<Spoiler
showLabel="👁️"
hideLabel="👁️"
showAriaLabel="Show discussion comments"
hideAriaLabel="Hide discussion comments"
>
{/* Comments content */}
</Spoiler>
);
}
Source-backed parameter reference
Implementation signatures and types are generated from the pinned local source. Optional props, native inherited types, union branches and never exclusions are preserved. A forwardRef implementation's ref is a React reference, not an additional business prop.
Spoiler(_props)
export type SpoilerStylesNames = 'root' | 'control' | 'content';
export type SpoilerCssVariables = {
root: '--spoiler-transition-duration';
};
export interface SpoilerProps
extends BoxProps, StylesApiProps<SpoilerFactory>, ElementProps<'div'> {
/** Maximum height of visible content in px. When content exceeds this height, the toggle control appears @default 100 */
maxHeight?: number;
/** Content displayed in the toggle button when content is collapsed (to expand) */
showLabel: React.ReactNode;
/** Content displayed in the toggle button when content is expanded (to collapse) */
hideLabel: React.ReactNode;
/** Ref of the toggle button element */
controlRef?: React.Ref<HTMLButtonElement>;
/** Initial expanded state in uncontrolled mode. If `true`, content starts expanded. If `false`, content starts collapsed @default false */
defaultExpanded?: boolean;
/** Controlled expanded state value */
expanded?: boolean;
/** Called when expanded state changes (when spoiler visibility is toggled by the user) */
onExpandedChange?: (expanded: boolean) => void;
/** Spoiler reveal transition duration in ms. Set to 0 to disable animation @default 200 */
transitionDuration?: number;
/** Accessible label for the toggle button when collapsed. If not set, `showLabel` is used */
showAriaLabel?: string;
/** Accessible label for the toggle button when expanded. If not set, `hideLabel` is used */
hideAriaLabel?: string;
}
export type SpoilerFactory = Factory<{
props: SpoilerProps;
ref: HTMLDivElement;
stylesNames: SpoilerStylesNames;
vars: SpoilerCssVariables;
}>;
export type Mod = Record<string, any> | string;
export type BoxMod = Mod | Mod[] | BoxMod[];
export interface BoxProps extends MantineStyleProps {
/** Class added to the root element, if applicable */
className?: string | undefined;
/** Inline style added to root component element, can subscribe to theme defined on MantineProvider */
style?: MantineStyleProp;
/** CSS variables defined on root component element */
__vars?: CssVarsProp;
/** `size` property passed down the HTML element */
__size?: string;
/** Breakpoint above which the component is hidden with `display: none` */
hiddenFrom?: MantineBreakpoint;
/** Breakpoint below which the component is hidden with `display: none` */
visibleFrom?: MantineBreakpoint;
/** Determines whether component should be hidden in light color scheme with `display: none` */
lightHidden?: boolean;
/** Determines whether component should be hidden in dark color scheme with `display: none` */
darkHidden?: boolean;
/** Element modifiers transformed into `data-` attributes, for example, `{ 'data-size': 'xl' }`, falsy values are removed */
mod?: BoxMod;
}
export type ElementProps<
ElementType extends React.ElementType,
PropsToOmit extends string = never,
> = Omit<React.ComponentProps<ElementType>, 'style' | PropsToOmit>;
export interface CSSProperties extends React.CSSProperties {
[key: string]: any;
}
type MantineStyle = CSSProperties | ((theme: MantineTheme) => CSSProperties);
export type MantineStyleProp = MantineStyle | MantineStyle[] | MantineStyleProp[] | undefined;
export type CssVariable = `--${string}`;
export type CssVariables<Variable extends string = CssVariable> = Partial<Record<Variable, string>>;
export type CssVars<Variable extends string = CssVariable> =
| CssVariables<Variable>
| ((theme: MantineTheme) => CssVariables<Variable>)
| CssVars<Variable>[];
export type CssVarsProp<Variable extends string = CssVariable> =
| CssVars<Variable>
| CssVars<Variable>[];
Native and external types
External types remain defined by React or native dependencies, not manually expanded into guessed props. The installed version's TypeScript definitions are authoritative.
Factory<{
props: SpoilerProps;
ref: HTMLDivElement;
stylesNames: SpoilerStylesNames;
vars: SpoilerCssVariables;
}>
MantineStyleProps
React.CSSProperties
MantineTheme
Partial<Record<Variable, string>>
Record<Variable, string>
Variable
MantineBreakpoint
Record<string, any>
StylesApiProps<SpoilerFactory>
React.ElementType
Omit<React.ComponentProps<ElementType>, 'style' | PropsToOmit>
React.ComponentProps<ElementType>
ElementType
PropsToOmit
React.ReactNode
React.Ref<HTMLButtonElement>
HTMLButtonElement
HTMLDivElement