Demos
How to import
import { Image, ImagePreview } from '@fibrous/semi-ui-react';
Basic usage
You can get an image with preview function by specifying the image path through src, and specify the width and height of the image through width, height

Code
import { Image } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
return <Image width={360} height={200} alt="Abstract colorful artwork" src="/assets/root-web-sites/abstract.jpg" />;
}Loading failed placeholder
You can customize the placeholder for failed loading through fallback, which supports string and ReactNode

自定义加载失败占位图

Code
import { IconUploadError } from '@fibrous/semi-icons-react';
import { Image } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
return (
<div style={{ display: 'flex', alignItems: 'center', flexDirection: 'column' }}>
<span>加载失败默认样式</span>
<Image width={200} height={200} src="https://load-error.jpeg" alt="加载失败的图片" />
<br />
<span>自定义加载失败占位图</span>
<Image width={200} height={200} src="https://load-error.jpeg" alt="加载失败的图片" fallback={<IconUploadError style={{ fontSize: 50 }} />} />
</div>
);
}Progressive loading
Large images can be progressively loaded through placeholder

Code
import React from 'react';
import { Image, Button } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
const [timestamp, setTimestamp] = React.useState('');
return (
<>
<Image width={300} height={200} alt="Abstract colorful artwork" src={`/assets/root-web-sites/abstract-big.png?${timestamp}`} placeholder={<Image src="/assets/root-web-sites/abstract-small.jpeg" width={300} height={200} alt="" preview={false} />} />
<br />
<Button
theme="solid"
onClick={() => {
setTimestamp(String(Date.now()));
}}
style={{ marginTop: 10 }}
>
Reload
</Button>
</>
);
}Customize the preview image
You can customize the preview image by setting the src of the Image component to be different from the src in the preview parameter

Code
import { Image } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
return (
<Image
width={300}
height={200}
alt="Abstract colorful artwork"
src="/assets/root-web-sites/abstract-small.jpeg"
preview={{
src: '/assets/root-web-sites/abstract-big.png',
}}
/>
);
}Multi-image preview
Use ImagePreview to wrap Image to achieve multi-image preview




Code
import { useMemo } from 'react';
import { Image, ImagePreview } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
const srcList = useMemo(() => ['/assets/root-web-sites/abstract.jpg', '/assets/root-web-sites/sky.jpg', '/assets/root-web-sites/greenleaf.jpg', '/assets/root-web-sites/colorful.jpg'], []);
return (
<ImagePreview>
{srcList.map((src, index) => {
return <Image key={index} src={src} width={200} alt={`lamp${index + 1}`} style={{ marginRight: 5 }} />;
})}
</ImagePreview>
);
}Use the preview component alone
The preview component ImagePreview can be used alone, through visible and onVisibleChange to control whether to preview, src to pass in the image that can be previewed, and previewTitle to set the title at the top of the preview
Code
import { useMemo, useCallback, useState } from 'react';
import { ImagePreview, Button } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
const srcList = useMemo(() => ['/assets/root-web-sites/abstract.jpg', '/assets/root-web-sites/sky.jpg', '/assets/root-web-sites/greenleaf.jpg', '/assets/root-web-sites/colorful.jpg'], []);
const [visible1, setVisible1] = useState(false);
const [visible2, setVisible2] = useState(false);
const visibleChange1 = useCallback((v: boolean) => {
setVisible1(v);
}, []);
const visibleChange2 = useCallback((v: boolean) => {
setVisible2(v);
}, []);
const onButton1Click = useCallback(() => {
setVisible1(true);
}, []);
const onButton2Click = useCallback(() => {
setVisible2(true);
}, []);
return (
<>
<Button onClick={onButton1Click}>Preview single Image</Button>
<ImagePreview src="/assets/root-web-sites/sky.jpg" visible={visible1} onVisibleChange={visibleChange1} />
<br />
<Button onClick={onButton2Click} style={{ marginTop: 20 }}>
Preview multiple Images
</Button>
<ImagePreview src={srcList} previewTitle="Scenery" visible={visible2} onVisibleChange={visibleChange2} />
</>
);
}Render in the specified container
You can specify the parent DOM of the preview component through getPopupContainer (you need to specify position: relative), and the image preview will be rendered into this DOM. This will change the DOM tree position, but not the view's rendering position.
When the container is not document.body (including a getPopupContainer set only on ConfigProvider), the preview is non-modal: it does not output aria-modal, does not trap Tab and does not lock page scrolling. Returning document.body from getPopupContainer is the same as not passing it.



Code
import { useMemo } from 'react';
import { Image, ImagePreview } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
const srcList = useMemo(() => ['/assets/root-web-sites/abstract.jpg', '/assets/root-web-sites/sky.jpg', '/assets/root-web-sites/greenleaf.jpg'], []);
return (
<>
<div
id="container"
style={{
height: 400,
position: 'relative',
}}
>
<ImagePreview
getPopupContainer={() => {
const node = document.getElementById('container') as HTMLElement;
return node;
}}
style={{
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{srcList.map((src, index) => {
return <Image key={index} src={src} width={200} alt={`lamp${index + 1}`} style={{ marginRight: 5 }} />;
})}
</ImagePreview>
</div>
</>
);
}Customize the bottom operation area of the preview
The bottom action area of the preview can be customized using renderPreviewMenu
If you want to customize the preview bottom operation area based on the default bottom operation area, you can get the default ReactNodes through the menuItems of renderPreviewMenu. menuItems is an array of ReactNodes, and the order is consistent with the content order of the default bottom operation bar area.
Customize the preview top display area
You can customize the preview top display area through renderHeader


Code
import { useMemo } from 'react';
import { Image, ImagePreview } from '@fibrous/semi-ui-react';
export default function ImageDemo() {
const srcList = useMemo(() => ['/assets/root-web-sites/abstract.jpg', '/assets/root-web-sites/sky.jpg'], []);
return (
<>
<ImagePreview
renderHeader={(title) => (
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
<span style={{ background: 'black', padding: '0 10px' }}>自定义标题:{title}</span>
</div>
)}
>
<div>
{srcList.map((src, index) => {
return (
<Image
key={index}
src={src}
width={200}
alt={`lamp${index + 1}`}
preview={{
previewTitle: `lamp${index + 1}`,
}}
style={{ marginRight: 5 }}
/>
);
})}
</div>
</ImagePreview>
</>
);
}API reference
Image
Props
| Property | Description | Type | Default |
|---|---|---|---|
| alt | Image description, also the accessible name of a previewable thumbnail. | string | - |
| children | Not rendered by the component, so setting it has no effect. | ReactNode | - |
| className | Class name of the thumbnail root element. | string | - |
| crossOrigin | The crossOrigin passed to the native img element, anonymous or use-credentials. When set, the preview image uses the same value; otherwise it uses preview.crossOrigin. | 'anonymous' | 'use-credentials' | - |
| fallback | Content shown when loading fails: a string is the fallback image URL, and a React element is shown as it is. Without it, the default failure icon is shown. | string | ReactNode | - |
| height | Display height of the image; a number is in px. | string | number | - |
| imgCls | Custom class name passed to the img element. | string | - |
| imgStyle | Custom style passed to the img element. | React.CSSProperties | - |
| onClick | Called when the image is clicked; bound to the inner img element. | Type(event: any) => void | - |
| placeholder | Placeholder content shown until the image has loaded; without it, a skeleton is shown. | ReactNode | - |
| preview | Preview options; false disables the preview. The object accepts the ImagePreview props except lazyLoad, lazyLoadMargin, children, className and style (use previewCls and previewStyle to style the preview). | boolean | ImagePreviewProps | true |
| ref | Reference to the inner img element. | TypeReact.Ref<HTMLImageElement> | - |
| setDownloadName | Sets the download file name: receives the image URL and returns the file name. | Type(src: string) => string | - |
| src | Image source URL. | string | - |
| style | Style of the thumbnail root element. | CSSProperties | - |
| width | Display width of the image; a number is in px. | string | number | - |
onLoad and onError are called when the image loads successfully or fails, with a SyntheticEvent<HTMLImageElement>; other native attributes (such as data-* and aria-*) pass through to the inner img node.
The preview object of a single Image accepts the ImagePreview props below, except lazyLoad, lazyLoadMargin, children, className and style (use previewCls and previewStyle to style the preview). When preview.src is an array, switching images returns to "fit to page".
ImagePreview
Props
| Property | Description | Type | Default |
|---|---|---|---|
| adaptiveTip | Tip of the fit-to-page button; without it, the locale text is used. | string | - |
| children | The Image components to preview. | ReactNode | - |
| className | Class name of the group root div. | string | - |
| closable | Whether to show the close button. | boolean | true |
| closeOnEsc | Whether pressing Esc closes the preview, true by default. | boolean | - |
| crossOrigin | The crossOrigin passed to the native img element of the preview image, anonymous or use-credentials. | 'anonymous' | 'use-credentials' | - |
| currentIndex | Controlled index of the current preview image. | number | - |
| defaultCurrentIndex | Index of the image shown initially. | number | - |
| defaultVisible | Whether the preview is open initially. | boolean | - |
| disableDownload | Disables downloads; the download button is also disabled when there is no current image (an empty src list). | boolean | - |
| downloadTip | Tip of the download button; without it, the locale text is used. | string | - |
| getPopupContainer | Parent DOM the preview renders into; returning document.body is the same as not passing it. A container other than document.body makes the preview non-modal, and a custom container needs position: relative. | Type() => HTMLElement | - |
| infinite | Whether switching wraps around infinitely, false by default. | boolean | - |
| initialZoom | Initial zoom factor of a preview image, applied once when it first opens or is switched to, clamped by minZoom and maxZoom. | number | - |
| lazyLoad | Whether to enable lazy loading, judged against the viewport; a scrolling ancestor with overflow still clips. | boolean | true |
| lazyLoadMargin | The rootMargin passed to IntersectionObserver, computed against the viewport. | string | "0px 100px 100px 0px" |
| maskClosable | Whether clicking the mask closes the preview, true by default. | boolean | - |
| maxZoom | Maximum zoom factor of a preview image, such as 5 for 500%, 5 by default. | number | - |
| minZoom | Minimum zoom factor of a preview image, such as 0.1 for 10%, 0.1 by default. | number | - |
| nextTip | Tip of the next-image button, also its accessible name; without it, the locale text is used. | string | - |
| onChange | Called when the image switches, with the new index. | Type(index: number) => void | - |
| onClose | Called when the preview is closed with the close button or Esc; closing by the mask only calls onVisibleChange, not onClose. | Type() => void | - |
| onDownload | Called when an image is downloaded, with its URL and index. | Type(src: string, index: number) => void | - |
| onDownloadError | Called when a download fails, with the image URL. | Type(src: string) => void | - |
| onNext | Called when switching to the next image, with the new index. | Type(index: number) => void | - |
| onPrev | Called when switching to the previous image, with the new index. | Type(index: number) => void | - |
| onRatioChange | Called when switching between original size and fit-to-page, with 'adaptation' or 'realSize'. | Type(type: RatioType) => void | - |
| onRotateLeft | Called when the image rotates counterclockwise, with the current rotation angle. | Type(angle: number) => void | - |
| onVisibleChange | Called when the visibility changes, with whether the preview is visible. | Type(visible: boolean) => void | - |
| onZoomIn | Called when the image zooms in, with the new zoom factor. | Type(zoom: number) => void | - |
| onZoomOut | Called when the image zooms out, with the new zoom factor. | Type(zoom: number) => void | - |
| originTip | Tip of the original-size button; without it, the locale text is used. | string | - |
| preLoad | Whether to enable preloading, true by default. | boolean | - |
| preLoadGap | Step size of preloading, 2 by default. | number | - |
| previewCls | Custom class name of the preview layer. | string | - |
| previewStyle | Custom style of the preview layer. | React.CSSProperties | - |
| previewTitle | Title at the top of the preview, also the accessible name of the preview dialog; the preview.previewTitle of an image in a group takes precedence. | ReactNode | - |
| prevTip | Tip of the previous-image button, also its accessible name; without it, the locale text is used. | string | - |
| ref | Reference to the group root div. | TypeReact.Ref<HTMLDivElement> | - |
| renderCloseIcon | Custom close icon: a React element, or a function that returns one. | TypeReact.ReactElement | (() => React.ReactElement) | - |
| renderHeader | Renders the top area of the preview; it receives the current title and returns the content to show. | Type(info: any) => ReactNode | - |
| renderLeftIcon | Custom previous-image icon: a React element, or a function that receives the current index and returns one. | TypeReact.ReactElement | ((index: number) => React.ReactElement) | - |
| renderPreviewMenu | Renders the bottom action area of the preview; it receives MenuProps and returns the content to show. | Type(props: MenuProps) => ReactNode | - |
| renderRightIcon | Custom next-image icon: a React element, or a function that receives the current index and returns one. | TypeReact.ReactElement | ((index: number) => React.ReactElement) | - |
| rotateTip | Tip of the rotate button; without it, the locale text is used. | string | - |
| setDownloadName | Sets the download file name: receives the image URL and returns the file name. | Type(src: string) => string | - |
| showTooltip | Whether to show tooltips on the bottom action area, false by default. | boolean | - |
| src | Image URLs to preview. When wrapping Image components, it is merged with the preview images they register, with the src entries first. | string | string[] | [] |
| style | Style of the group root div. | CSSProperties | - |
| viewerVisibleDelay | Idle time in milliseconds before the preview action buttons hide, 10000 by default. | number | - |
| visible | Controlled visibility of the preview. | boolean | - |
| zIndex | z-index of the preview layer, 1070 by default. | number | - |
| zoomInTip | Tip of the zoom-in button; without it, the locale text is used. | string | - |
| zoomOutTip | Tip of the zoom-out button; without it, the locale text is used. | string | - |
| zoomStep | Zoom factor changed per zoom step, such as 0.1 for 10% per step, 0.1 by default. | number | - |
Other native attributes (such as aria-label and aria-labelledby) land on the group root div; with aria-label or aria-labelledby and no role, the root gets role="group". The preview dialog is rendered into getPopupContainer (document.body by default) and is named by previewTitle, or by the locale when there is no title.
MenuProps
min, max, step and zoom are percentages (a min of 10 means 10%, a step of 10 means 10% per step), unlike minZoom, maxZoom, zoomStep and initialZoom of ImagePreview, which are factors (such as 0.1 and 5).
| Properties | Instructions | Type |
|---|---|---|
| curPage | Current image page subscript | number |
| disabledPrev | Whether to disable the left toggle button | boolean |
| disabledNext | Whether to disable the right toggle button | boolean |
| disabledZoomIn | Whether the zoom is at its maximum (the zoom-in button is disabled) | boolean |
| disabledZoomOut | Whether the zoom is at its minimum (the zoom-out button is disabled) | boolean |
| disableDownload | Whether to disable the download button | boolean |
| max | The maximum zoom of the image (percentage) | number |
| min | The minimum zoom of the image (percentage) | number |
| onDownload | Call function when the image is downloaded | () => void |
| onZoomIn | Call function when the image is zoomed in | () => void |
| onZoomOut | Call function when the image is zoomed out | () => void |
| onRatioClick | Call function to switch between the original size and fit to page | () => void |
| onPrev | Call function to switch the picture forward | () => void |
| onNext | Call function to switch the picture backward | () => void |
| onRotateLeft | Call function to rotate the image counterclockwise | () => void |
| onRotateRight | Call function to rotate the image clockwise | () => void |
| ratio | Original size or Fit to page button state | "adaptation" | "realSize" |
| step | Step size of zooming (percentage) | number |
| totalNum | The total number of images that can be previewed | number |
| zoom | Current zoom of the image (percentage) | number |
| menuItems | Default bottom preview operation area function button ReactNode array | ReactNode[] |
Accessibility
ARIA
- When rendered into
document.body(the default), the preview is a modal dialog withrole="dialog"andaria-modal="true"; rendered into a custom container (getPopupContainerreturns another element, including a container set on ConfigProvider) it is a non-modal dialog and does not outputaria-modal. It is named by its title (previewTitle, orpreview.previewTitleof an image in a group) when there is one, otherwise by "Preview" from the locale - Close, the left and right arrows, and the footer's previous, next, zoom out, zoom in, original size / adapt to the page, rotate and download controls are native
buttonelements. Their accessible names come from the matchingprevTip,nextTipand other tip props, or from the locale when those are not set. The zoom slider is named "Zoom" from the locale, with the current percentage as itsaria-valuetext - At the first or last image, or at a zoom limit, the matching footer button gets
aria-disabled="true"and stays focusable, so focus is not lost. The download button is also disabled when there is no current image (an emptysrclist) - The footer page counter has
aria-live="polite", so screen readers announce the new position when the image changes - With the preview enabled and the image loaded, the root of an Image thumbnail is a focusable button (
role="button",tabindex="0",aria-haspopup="dialog") named by thealtof the image; withpreviewset to false, or before the image has loaded, it is not focusable. Closing the preview returns focus to the thumbnail. A thumbnail inside an ImagePreview group that opens with the mouse also opens with the keyboard: while loading or after a load error it still has the button role and is focusable, and Enter or Space opens the group preview - The
altof the preview image is thealtof the thumbnail it shows (anImagePreviewsrcarray has no alt, so it is an empty string)
Keyboard interaction
- With focus on a thumbnail, Enter or Space opens the preview
- Opening the preview moves focus into it. In a modal preview, Tab and Shift + Tab cycle through its controls and closing it returns focus to the element focused before; a non-modal preview rendered into a custom container does not trap Tab, and on close focus returns to the element focused before only if it is still inside the preview or on body, and stays where it is when it has moved elsewhere on the page
- Enter or Space presses the focused button
- ← and → switch to the previous and next image, swapped in RTL (ConfigProvider
direction="rtl"). At the first or last image they behave like the buttons: they wrap around wheninfiniteis true and do nothing otherwise - ← and → do not switch images while focus is in an input, textarea, select or editable element (such as an input in a custom footer), or while Alt, Ctrl, Meta or Shift is held; on the focused zoom slider they change the zoom
- Esc closes the preview (unless
closeOnEscis false) - After the controls hide because of
viewerVisibleDelay, a key press or focus on a control shows them again; they stay shown while focus is on one of them
Design Token
| Sass variable | Value | Usage |
|---|---|---|
| $transform_rotate-image_preview_spinner | var(--semi-transform_rotate-clockwise360deg) | 预览图像加载spin旋转角度 |
| $transform_scale3d-image_preview_image_img | 1, 1, 1 | 预览图片放大 |
| $transform_rotate-image_preview_image_img | var(--semi-transform_rotate-none) | 预览图片旋转角度 |
| $transition_duration-image_preview | 500ms | 预览图片透明度变化动画时间 |
| $transform_rotate-image_preview_icon_rtl | var(--semi-transform_rotate-clockwise180deg) | rtl模式下向前/向后切换图片按钮旋转角度 |
| $spacing-image_preview_icon-x | 24px | 图像预览中部左右调节icon与页面距离 |
| $spacing-image_preview_header-paddingY | 0 | 图像预览header部分上下内边距 |
| $spacing-image_preview_header-paddingX | 24px | 图像预览header部分左右内边距 |
| $spacing-image_preview_footer-paddingY | 0 | 图像预览footer操作区部分上下内边距 |
| $spacing-image_preview_footer-paddingX | 16px | 图像预览footer操作区部分左右内边距 |
| $spacing-image_preview_footer_page-marginY | 0 | 图像预览footer操作区图像页数据上下内边距 |
| $spacing-image_preview_footer_page-marginX | 12px | 图像预览footer操作区图像页数据左右内边距 |
| $spacing-image_preview_footer_slider-paddingY | 0 | 图像预览footer操作区slider上下内边距 |
| $spacing-image_preview_footer_slider-paddingX | 16px | 图像预览footer操作区slider左右内边距 |
| $spacing-image_preview_footer_slider_handle-marginTop | 8px | 图像预览footer操作区slider的滑块上外边距 |
| $spacing-image_preview_footer_divider-marginY | 0 | 图像预览footer操作区slider的分割线上下外边距 |
| $spacing-image_preview_footer_divider-marginX | 16px | 图像预览footer操作区slider的分割线左右外边距 |
| $spacing-image_preview_footer_gap-marginLeft | 16px | 图像预览footer操作区icon的左外边距 |
| $width-image_preview_footer_slider | 132px | 图像预览footer操作区slider宽度 |
| $width-image_preview_footer_slider_handle | 16px | 图像预览footer操作区滑块宽度 |
| $width-image_preview_icon | 40px | 图像预览footer操作区icon宽度 |
| $width-image_preview_header_close | 30px | 图像预览header部分的关闭热区宽度 |
| $height-image_preview_header | 60px | 图像预览header部分高度 |
| $height-image_preview_footer | 48px | 图像预览footer部分高度 |
| $height-image_preview_footer_slider | 2px | 图像预览footer中slider高度 |
| $height-image_preview_footer_slider_handle | 16px | 图像预览footer中slider的滑块高度 |
| $height-image_preview_icon | 40px | 图像预览footer操作区icon高度 |
| $height-image_preview_header_close | 30px | 图像预览header部分的关闭热区高度 |
| $radius-image | var(--semi-border-radius-small) | 图像圆角 |
| $radius-image_preview_footer | var(--semi-border-radius-medium) | 图像预览footer操作区圆角 |
| $color-image_status-bg | var(--semi-color-fill-0) | 图像加载失败背景颜色 |
| $color-image_status | var(--semi-color-disabled-text) | 图像状态加载失败 icon 颜色 |
| $color-image_preview_icon | var(--semi-color-white) | 图像预览中部左右icon背景色 |
| $color-image_preview_header | var(--semi-color-white) | 图像预览header文字颜色 |
| $color-image_preview_footer_icon | var(--semi-color-white) | 图像预览footer中icon颜色 |
| $color-image_preview_footer_slider_rail | var(--semi-color-white) | 图像预览footer中slider滑轨颜色 |
| $color-image_preview_disabled | rgba(249, 249, 249, 0.35) | 图像预览禁用颜色 |
| $color-image_preview_icon-bg | rgba(0, 0, 0, 0.75) | 图像预览中部icon背景色 |
| $color-image_header_close-bg | rgba(0, 0, 0, 0.75) | 图像预览header的关闭icon的hover背景色 |
| $color-image_preview_footer-bg | rgba(0, 0, 0, 0.75) | 图像预览footer部分背景色 |
| $color-image-preview_divider-bg | rgba(255, 255, 255, 0.5) | 图像预览footer中的分割线背景色 |
| $color-image_preview_image_spin | #ccc | 图像预览的加载状态颜色 |