React UI component library behind ONLYOFFICE Apps.
@onlyoffice/apps-ui-kit provides the React components and color system used across the ONLYOFFICE Apps frontend products. It began life inside the DocSpace-client monorepo and is now developed as a standalone package.
Note: version
4.0.0renamed the package, made it ESM-only and put anexportsmap in front of every subpath. See CHANGELOG.md.
New here?
docs/getting-started.mdis the one page to read first: what to install, the two providers to mount, why the stylesheet needs no import, and which import form to use.docs/components.mdis the catalogue, with a table for choosing between the components that are easy to confuse.
Scope: not every directory in this package is public API. Modules coupled to an ONLYOFFICE Apps portal —
api/,billing/,selectors/,uploader/,ai-agent/,document-editor/andproviders/api— ship in the package but are intended for ONLYOFFICE's own products. Seedocs/public-api.md.
- TypeScript-first - Full type definitions included out of the box
- Color system - Built-in
globalColorspalette with named color tokens for light and dark UI states - Interactive docs - Every component is documented with Storybook stories and controls
- Tree-shakeable - ESM only, one module per source file; import only what you use
- Well-tested - Components are tested with Vitest and React Testing Library
- Modern tooling - Built with Rollup, linted and formatted with Biome
- React ^19.0.0
- React DOM ^19.0.0
# pnpm
pnpm add @onlyoffice/apps-ui-kit
# npm
npm install @onlyoffice/apps-ui-kit
# yarn
yarn add @onlyoffice/apps-ui-kitTo try a build of this repository instead, pack it and install the file:
pnpm build && pnpm pack
pnpm add file:../path/to/onlyoffice-apps-ui-kit-4.0.1.tgzPeer dependencies: four are required — react and react-dom ^19.0.0, plus i18next
and react-i18next, which the translated components need and which break silently if a
second copy ends up in the tree. Fifteen more are optional and only matter to the modules
that use them: mobx / mobx-react and react-router for the portal-coupled modules,
axios for providers/api, socket.io-client for utils/socket,
@onlyoffice/document-editor-react for document-editor/, and @onlyoffice/ai-chat with
the markdown and KaTeX stack for ai-agent/. An install downloads none of the optional
set. See peerDependencies in package.json.
axiosis the exception worth knowing about: the main entry reaches it throughuploaderandbilling, so a consumer that bundles the barrel has to install it even though it is declared optional. Tracked indocs/public-api.md.
Styles come with the components: every module imports its own CSS file, so a
bundler ships exactly the styles of the components you import, split along the
same chunks as the code. Nothing has to be imported by hand. The whole-library
stylesheet is still published for setups that cannot process CSS imports from
node_modules; importing it loads every component's styles at once:
import "@onlyoffice/apps-ui-kit/styles.css";import { Text } from "@onlyoffice/apps-ui-kit";
function App() {
return (
<Text fontSize="14px" isBold>
Hello World
</Text>
);
}import { globalColors } from "@onlyoffice/apps-ui-kit/providers/theme";
const primaryBlue = globalColors.lightBlueMain;
const errorRed = globalColors.mainRed;import {
Aside,
AsideHeader,
Avatar,
Backdrop,
Badge,
Button,
Calendar,
Checkbox,
CircleSkeleton,
ComboBox,
ContextMenu,
ContextMenuButton,
DatePicker,
DateTimePicker,
DropDown,
DropDownItem,
EmailInput,
EmptyScreenContainer,
EmptyView,
FieldContainer,
FileInput,
Heading,
HelpButton,
IconButton,
InputBlock,
Label,
Link,
Loader,
LoadingButton,
MCPIcon,
ModalDialog,
Portal,
ProgressBar,
RadioButton,
RadioButtonGroup,
RectangleSkeleton,
RoomIcon,
Row,
RowContainer,
RowContent,
Scrollbar,
SearchInput,
SelectedItem,
SelectionArea,
Slider,
SnackBar,
TabItem,
Tabs,
Tag,
Tags,
Text,
TextInput,
Textarea,
ThemeProvider,
TimePicker,
Toast,
toastr,
ToggleButton,
Tooltip,
TopLoaderService,
BaseTile,
FileTile,
FolderTile,
RoomTile,
TemplateTile,
TileContainer,
TileContent,
} from "@onlyoffice/apps-ui-kit";Six components that look like they belong in this list are not reachable from the main entry today:
FilterInput,LoaderWrapper,PublicRoomBar,RoomLogo,SelectorandStatusMessage. Three are default exports, whichexport *does not re-export; the other folders are not registered incomponents/index.ts. Import them by subpath —import { Selector } from "@onlyoffice/apps-ui-kit/components/selector",import FilterInput from "@onlyoffice/apps-ui-kit/components/filter"— and note that an ONLYOFFICE Apps plugin cannot reach them at all, since the portal gives plugins the main entry and refuses every subpath.
import { AccessRightSelect } from "@onlyoffice/apps-ui-kit/components/access-right-select";
import { Aside, AsideHeader } from "@onlyoffice/apps-ui-kit/components/aside";
import { Avatar } from "@onlyoffice/apps-ui-kit/components/avatar";
import { Backdrop } from "@onlyoffice/apps-ui-kit/components/backdrop";
import { Badge } from "@onlyoffice/apps-ui-kit/components/badge";
import { Button, ButtonSize } from "@onlyoffice/apps-ui-kit/components/button";
import { Calendar } from "@onlyoffice/apps-ui-kit/components/calendar";
import { CategoryItem } from "@onlyoffice/apps-ui-kit/components/category-item";
import { Checkbox } from "@onlyoffice/apps-ui-kit/components/checkbox";
import { CircleSkeleton } from "@onlyoffice/apps-ui-kit/components/circle";
import { ColorInput } from "@onlyoffice/apps-ui-kit/components/color-input";
import { ColorPicker } from "@onlyoffice/apps-ui-kit/components/color-picker";
import { ComboBox } from "@onlyoffice/apps-ui-kit/components/combobox";
import { ContextMenu } from "@onlyoffice/apps-ui-kit/components/context-menu";
import {
ContextMenuButton,
ContextMenuButtonDisplayType,
} from "@onlyoffice/apps-ui-kit/components/context-menu-button";
import { DatePicker } from "@onlyoffice/apps-ui-kit/components/date-picker";
import { DateTimePicker } from "@onlyoffice/apps-ui-kit/components/date-time-picker";
import { DragAndDrop } from "@onlyoffice/apps-ui-kit/components/drag-and-drop";
import { DropDown } from "@onlyoffice/apps-ui-kit/components/drop-down";
import { DropDownItem } from "@onlyoffice/apps-ui-kit/components/drop-down-item";
import { Dropzone } from "@onlyoffice/apps-ui-kit/components/dropzone";
import { EmailInput } from "@onlyoffice/apps-ui-kit/components/email-input";
import { EmptyScreenContainer } from "@onlyoffice/apps-ui-kit/components/empty-screen-container";
import { EmptyView } from "@onlyoffice/apps-ui-kit/components/empty-view";
import { ErrorContainer } from "@onlyoffice/apps-ui-kit/components/error-container";
import { FieldContainer } from "@onlyoffice/apps-ui-kit/components/field-container";
import { FileInput } from "@onlyoffice/apps-ui-kit/components/file-input";
import { FilterInput } from "@onlyoffice/apps-ui-kit/components/filter";
import {
FloatingButton,
FloatingButtonIcons,
} from "@onlyoffice/apps-ui-kit/components/floating-button";
import { FormWrapper } from "@onlyoffice/apps-ui-kit/components/form-wrapper";
import {
Heading,
HeadingLevel,
HeadingSize,
} from "@onlyoffice/apps-ui-kit/components/heading";
import { HelpButton } from "@onlyoffice/apps-ui-kit/components/help-button";
import { IconButton } from "@onlyoffice/apps-ui-kit/components/icon-button";
import { ImageEditor } from "@onlyoffice/apps-ui-kit/components/image-editor";
import { InfiniteLoaderComponent } from "@onlyoffice/apps-ui-kit/components/infinite-loader";
import { InputBlock } from "@onlyoffice/apps-ui-kit/components/input-block";
import { Label } from "@onlyoffice/apps-ui-kit/components/label";
import {
Link,
LinkType,
LinkTarget,
} from "@onlyoffice/apps-ui-kit/components/link";
import { LinkWithDropdown } from "@onlyoffice/apps-ui-kit/components/link-with-dropdown";
import { Loader, LoaderTypes } from "@onlyoffice/apps-ui-kit/components/loader";
import { LoaderWrapper } from "@onlyoffice/apps-ui-kit/components/loader-wrapper";
import { LoadingButton } from "@onlyoffice/apps-ui-kit/components/loading-button";
import { MainButton } from "@onlyoffice/apps-ui-kit/components/main-button";
import { MainButtonMobile } from "@onlyoffice/apps-ui-kit/components/main-button-mobile";
import {
MCPIcon,
MCPIconSize,
} from "@onlyoffice/apps-ui-kit/components/mcp-icon";
import {
ModalDialog,
ModalDialogType,
} from "@onlyoffice/apps-ui-kit/components/modal-dialog";
import { Navigation } from "@onlyoffice/apps-ui-kit/components/navigation";
import { OperationsProgressButton } from "@onlyoffice/apps-ui-kit/components/operations-progress-button";
import { Paging } from "@onlyoffice/apps-ui-kit/components/paging";
import { PasswordInput } from "@onlyoffice/apps-ui-kit/components/password-input";
import { Portal } from "@onlyoffice/apps-ui-kit/components/portal";
import { PortalLogo } from "@onlyoffice/apps-ui-kit/components/portal-logo";
import {
ProgressBar,
PreparationPortalProgress,
} from "@onlyoffice/apps-ui-kit/components/progress-bar";
import { PublicRoomBar } from "@onlyoffice/apps-ui-kit/components/public-room-bar";
import { RadioButton } from "@onlyoffice/apps-ui-kit/components/radio-button";
import { RadioButtonGroup } from "@onlyoffice/apps-ui-kit/components/radio-button-group";
import { RectangleSkeleton } from "@onlyoffice/apps-ui-kit/components/rectangle";
import { RoomIcon } from "@onlyoffice/apps-ui-kit/components/room-icon";
import { RoomLogo } from "@onlyoffice/apps-ui-kit/components/room-logo";
import {
Row,
RowContainer,
RowContent,
} from "@onlyoffice/apps-ui-kit/components/rows";
import { Scrollbar } from "@onlyoffice/apps-ui-kit/components/scrollbar";
import { SearchInput } from "@onlyoffice/apps-ui-kit/components/search-input";
import { Section } from "@onlyoffice/apps-ui-kit/components/section";
import { SelectedItem } from "@onlyoffice/apps-ui-kit/components/selected-item";
import { SelectionArea } from "@onlyoffice/apps-ui-kit/components/selection-area";
import {
Selector,
SelectorAccessRightsMode,
} from "@onlyoffice/apps-ui-kit/components/selector";
import { Slider } from "@onlyoffice/apps-ui-kit/components/slider";
import { SnackBar } from "@onlyoffice/apps-ui-kit/components/snackbar";
import { StatusMessage } from "@onlyoffice/apps-ui-kit/components/status-message";
import { TabItem } from "@onlyoffice/apps-ui-kit/components/tab-item";
import {
TableContainer,
TableBody,
TableRow,
TableHeader,
TableGroupMenu,
TableCell,
} from "@onlyoffice/apps-ui-kit/components/table";
import { Tabs, TabsTypes } from "@onlyoffice/apps-ui-kit/components/tabs";
import { Tag } from "@onlyoffice/apps-ui-kit/components/tag";
import { Tags } from "@onlyoffice/apps-ui-kit/components/tags";
import { Text } from "@onlyoffice/apps-ui-kit/components/text";
import {
TextInput,
InputSize,
InputType,
} from "@onlyoffice/apps-ui-kit/components/text-input";
import { Textarea } from "@onlyoffice/apps-ui-kit/components/textarea";
import { ThemeProvider } from "@onlyoffice/apps-ui-kit/components/theme-provider";
import { TimePicker } from "@onlyoffice/apps-ui-kit/components/time-picker";
import { Toast, toastr } from "@onlyoffice/apps-ui-kit/components/toast";
import { ToggleButton } from "@onlyoffice/apps-ui-kit/components/toggle-button";
import {
Tooltip,
TooltipContainer,
withTooltip,
} from "@onlyoffice/apps-ui-kit/components/tooltip";
import { TopLoaderService } from "@onlyoffice/apps-ui-kit/components/top-loading-indicator";
import {
BaseTile,
FileTile,
FolderTile,
RoomTile,
TemplateTile,
TileContainer,
TileContent,
} from "@onlyoffice/apps-ui-kit/components/tiles";import {
ThemeProvider,
useTheme,
} from "@onlyoffice/apps-ui-kit/context/ThemeContext";
import {
InterfaceDirectionProvider,
useInterfaceDirection,
} from "@onlyoffice/apps-ui-kit/context/InterfaceDirectionContext";
import { useAnimation } from "@onlyoffice/apps-ui-kit/hooks/useAnimation";
import { useDebounce } from "@onlyoffice/apps-ui-kit/hooks/useDebounce";
import { useEventListener } from "@onlyoffice/apps-ui-kit/hooks/useEventListener";
import { useIsomorphicLayoutEffect } from "@onlyoffice/apps-ui-kit/hooks/useIsomorphicLayoutEffect";import { Providers } from "@onlyoffice/apps-ui-kit/providers";
import { ApiProvider, useApi } from "@onlyoffice/apps-ui-kit/providers/api";
import { ErrorBoundary } from "@onlyoffice/apps-ui-kit/providers/error-boundary";
import { TranslationProvider, useI18N } from "@onlyoffice/apps-ui-kit/providers/translation";
import { ThemeProvider } from "@onlyoffice/apps-ui-kit/providers/theme";
import type { TTranslations } from "@onlyoffice/apps-ui-kit/providers/translation";import {
Error401,
Error403,
Error404,
ErrorOfflineContainer,
ErrorInvalidLink,
ErrorUnavailable,
AccessRestricted,
} from "@onlyoffice/apps-ui-kit/errors";import {
isMobile,
isTablet,
isDesktop,
checkIsSSR,
} from "@onlyoffice/apps-ui-kit/utils";
import {
IconSizeType,
isIconSizeType,
} from "@onlyoffice/apps-ui-kit/utils/common-icons-style";
import DomHelpers from "@onlyoffice/apps-ui-kit/utils/dom-helpers";
import { useClickOutside } from "@onlyoffice/apps-ui-kit/utils/use-click-outside";
import { getCommonTranslation } from "@onlyoffice/apps-ui-kit/utils/i18n";
import { combineUrl } from "@onlyoffice/apps-ui-kit/utils/combineUrl";
import {
getCookie,
setCookie,
deleteCookie,
} from "@onlyoffice/apps-ui-kit/utils/cookie";
import {
parseAddress,
parseAddresses,
EmailSettings,
} from "@onlyoffice/apps-ui-kit/utils/email";import { ShareAccessRights, EmployeeType, RoomsType, ThemeKeys, FileType, FolderType } from "@onlyoffice/apps-ui-kit/enums";
import { LOADER_STYLE, OPERATIONS_NAME, EMPTY_ARRAY, EMPTY_OBJECT } from "@onlyoffice/apps-ui-kit/constants";
import type { TFile, TFolder, TUser, TFileSecurity, TFolderSecurity, TRoomSecurity } from "@onlyoffice/apps-ui-kit/types";| Component | Description |
|---|---|
| AccessRightSelect | Dropdown selector for managing access rights and permissions on resources |
| AddButton | Button component for adding items with optional label, loading state, and accent styling |
| AppLoader | Full-page loading spinner displayed during application initialization |
| Article | Responsive sidebar panel with header, main button, and body sections for navigation |
| Aside | Sliding panel component for displaying side content like settings, details, or forms |
| AsideHeader | Header component for aside panels with optional back/close buttons, custom icons, and loading states |
| Avatar | Component for displaying user or group avatars with images, initials, icons, and role indicators |
| Backdrop | Customizable overlay for modals, dialogs, and aside components with touch support |
| Badge | Versatile badge for notifications, status markers, or interactive elements with various display modes |
| Button | Versatile button component with primary/secondary variants, multiple sizes, loading states, and tooltip support |
| Calendar | Custom calendar component for date selection |
| CategoryItem | Navigation link item with optional badge and arrow indicator for menu hierarchies |
| Checkbox | Customizable checkbox with indeterminate and error states |
| CircleSkeleton | Circular skeleton loader for avatar and icon placeholders |
| ColorInput | Text input for entering and validating color values |
| ColorPicker | Interactive color selection component for choosing colors visually |
| ComboBox | Combo box combining text input with dropdown list, supporting search and custom styling |
| ContextMenu | Context menu for displaying contextual actions with submenus, headers, toggles, and hotkeys |
| ContextMenuButton | Button for displaying context menu actions on list items with dropdown support |
| DatePicker | Date picker input component for selecting dates |
| DateTimePicker | Combined date and time input component |
| DragAndDrop | File drag-and-drop handler for upload operations |
| DropDown | Dropdown component for menus, options, and contextual content with auto-positioning |
| DropDownItem | Dropdown item for menus and lists with separator, header, submenu, and toggle support |
| Dropzone | Interactive file drop area with visual feedback and file type restrictions |
| EmailInput | Email address input with built-in validation and error reporting |
| EmptyScreenContainer | Component for displaying empty states with image, text, and action buttons |
| EmptyView | Empty state component with icon, title, description, and interactive options |
| ErrorContainer | Full-page error display with decorative background, message, and action button |
| FieldContainer | Container wrapper for form fields with consistent spacing and styling |
| FileInput | File entry field |
| FilterInput | Filter component with search, sorting, and view options for data filtering |
| FloatingButton | Circular floating action button with progress indicator and alert icon |
| FormWrapper | Wrapper for form elements providing consistent form styling and layout |
| Heading | Heading text structured in levels with customizable sizes and types |
| HelpButton | Info icon button that triggers a tooltip with help content |
| IconButton | Button component displaying an icon with hover, click, and disabled states |
| ImageEditor | Image cropping and editing component with file upload and preview support |
| InfiniteLoader | Virtualized infinite scroll container for loading items progressively |
| InputBlock | Input component combining text input with optional icon and children elements |
| Label | Form label with required indicator and tooltip support |
| Link | Hyperlink component with page and action types |
| LinkWithDropdown | Link that opens a dropdown menu with additional options |
| Loader | Loading indicator with multiple animation types |
| LoaderWrapper | Wrapper that dims children and blocks interactions during loading |
| LoadingButton | Circular button with animated progress bar and completion indicator |
| MainButton | Primary action button with optional dropdown menu for desktop |
| MainButtonMobile | Floating action button with expandable menu for mobile interface |
| MCPIcon | Icon component for MCP (Model Context Protocol) with image or text fallback |
| ModalDialog | Versatile modal dialog component supporting both modal and aside (side panel) display types with keyboard shortcuts |
| Navigation | Breadcrumb navigation with expandable hierarchical sections |
| NewChat | AI chat interface backed by the @onlyoffice/ai-chat package with threads, streaming responses, file attachments, and tool calls |
| OperationsProgressButton | Operation progress tracker for multiple concurrent operations with error checking |
| Paging | Pagination controls for navigating through pages of data |
| PasswordInput | Password input with strength validation, generator, and reveal/hide toggle |
| Portal | Renders children into a different DOM node |
| PortalLogo | Responsive logo component with fallback handling |
| ProgressBar | Visual progress indicator bar and portal preparation progress display |
| PublicRoomBar | Information bar for public room notifications with header, body text, and close button |
| RadioButton | Radio button component with customizable labels and styles |
| RadioButtonGroup | Group of radio buttons with horizontal/vertical layouts and text labels |
| RectangleSkeleton | Rectangular skeleton loader for text, buttons, and content placeholders |
| RoomIcon | Room icon component with support for images, colors, badges, editing, and various states |
| RoomLogo | Room logo component displaying room type icons with archive, template, and checkbox support |
| Row | Versatile list row with checkbox, context menu, badges, and modern/default layouts for file or member listings |
| RowContainer | Wrapper for rendering multiple rows with optional react-window virtualization and infinite loading |
| RowContent | Layout helper that arranges row title, icons, and side sections while generating tablet-friendly summaries |
| Scrollbar | Custom scrollbar component with auto-hide, RTL support, and flexible styling options |
| SearchInput | Search input component with auto-refresh, clear button, and debounce support |
| Section | Page layout with header, filter, body, footer, info panel, and operations progress sections |
| SelectedItem | Component for displaying selected items with remove functionality |
| SelectionArea | Mouse-driven rectangular selection area for selecting multiple items |
| Selector | Dropdown selector with access rights modes for item selection with permissions |
| Slider | Range input slider with optional fill visualization and RTL support |
| SnackBar | Toast notification component for temporary messages and alerts |
| StatusMessage | Animated status message component for displaying error and warning messages |
| TabItem | Tab navigation component with active states, multi-select, and disabled state support |
| TableContainer | Modular table with body, row, header, group menu, and cell sub-components |
| Tabs | Tab container component with primary/secondary themes, sticky positioning, and content management |
| Tag | Tag component for displaying virtual room tags |
| Tags | Container component for displaying multiple tags |
| Text | Typography component with various styling options |
| TextInput | Input field for single-line strings with masking support |
| Textarea | Multi-line text input with JSON formatting, line numbers, and copy functionality |
| ThemeProviderComponent | The older theme provider; new code uses ThemeProvider from providers/theme |
| TimePicker | Time input component for selecting time values |
| Toast | Notification component with success, error, warning, and info variants (see i18n Setup) |
| ToggleButton | Customizable toggle button with loading and disabled states |
| Tooltip | Customizable tooltip with multiple trigger options |
| TopLoaderService | Top page progress bar service with start(), end(), cancel() static methods |
| BaseTile | Base tile component providing foundational structure for all tile components |
| FileTile | File tile component for displaying file information with thumbnail preview |
| FolderTile | Folder tile component with support for compact and big folder views |
| RoomTile | Room tile component for displaying room information with tags and metadata |
| TemplateTile | Template tile component with owner and storage metadata |
| TileContainer | Container for organizing tiles in a grid layout with automatic categorization |
| TileContent | Content wrapper component for tile children with consistent styling |
| Context | Description |
|---|---|
| ThemeContext | Theme management context with support for Base/Dark themes and custom color schemes |
| InterfaceDirectionContext | Interface direction context for managing LTR/RTL layout support |
| Hook | Description |
|---|---|
| useAnimation | Manages animation state with phases (none/start/progress/finish), dispatches custom window events, and returns refs and triggerAnimation() |
| useDebounce | Debounces callback execution with configurable delay and automatic cleanup on unmount |
| useEventListener | Typed event listener hook for Window, HTML/SVG elements, Document, and MediaQueryList with automatic cleanup |
| useIsomorphicLayoutEffect | SSR-safe effect hook — uses useLayoutEffect in browser, useEffect on server |
Composed providers that wrap your application with error handling, API access, translations, and theming.
| Provider | Description |
|---|---|
| Providers | All-in-one composition of ErrorBoundary, ApiProvider, TranslationProvider, and ThemeProvider |
| ApiProvider | Creates React Context with profilesApi and commonSettingsApi from @onlyoffice/docspace-api-sdk |
| ErrorBoundary | Class component that catches render errors with custom fallback UI and onError callback |
| TranslationProvider | Wraps I18nextProvider and initializes i18next with translation resources |
| ThemeProvider | Resolves theme (Base/Dark/System), fetches color themes, monitors system preferences |
import { Providers } from "@onlyoffice/apps-ui-kit/providers";
import enCommon from "@onlyoffice/apps-ui-kit/locales/en/Common.json";
import type { TTranslations } from "@onlyoffice/apps-ui-kit/providers/translation";
const translations: TTranslations = new Map([
["en", new Map([["Common", enCommon]])],
]);
<Providers
url="https://your-docspace.com"
apiKey="your-api-key"
translations={translations}
locale="en"
>
<App />
</Providers>;See Providers with i18n for full internationalization guide.
Pre-built error page components that wrap ErrorContainer with localized messages via getCommonTranslation.
| Component | Translation Key | Description |
|---|---|---|
| Error401 | Error401Text |
Unauthorized access error |
| Error403 | Error403Text |
Forbidden access error |
| Error404 | Error404Text |
Page not found error |
| ErrorOfflineContainer | ErrorOfflineText |
Offline / no connection error |
| ErrorInvalidLink | InvalidLink, LinkDoesNotExist |
Invalid or expired link error |
| ErrorUnavailable | ErrorDeactivatedText |
Portal deactivated error |
| AccessRestricted | AccessDenied, PortalRestriction |
Access restricted error |
import { Error401, Error403, Error404 } from "@onlyoffice/apps-ui-kit/errors";| Utility | Description |
|---|---|
| common-icons-style | Styled-components CSS helper for consistent icon sizing with IconSizeType enum |
| combineUrl | Merges base URL with path segments, handling trailing/leading slashes |
| common | getUserTypeTranslation for employee role types, RoomsTypeValues, RoomsTypes helpers |
| context | React context with optional sectionWidth and sectionHeight |
| cookie | getCookie, setCookie, deleteCookie — cookie management with asc_language special handling |
| date | Date utilities: formatting, arithmetic, comparison, duration, timezone, and parsing |
| device | Device detection utilities: isMobile, isTablet, isDesktop, checkIsSSR |
| dom-helpers | DOM utilities for viewport, element positioning, scrollbar width, and z-index management |
| edge-scrolling | Auto-scrolls when mouse is near viewport edges — used for drag-and-drop operations |
Email parsing and validation: parseAddress, parseAddresses, EmailSettings, isValidDomainName |
|
| get-system-theme | Returns system theme preference (Dark or Base) from AscDesktopEditor or prefers-color-scheme |
| get-text-color | Determines optimal text color (black/white) for a background based on perceived brightness |
| getFilesFromEvent | Converts drag/drop/paste/input events to File arrays with recursive directory handling |
| getLogoUrl | Generates logo URL using WhiteLabelLogoType enum with culture and theme parameters |
| hasOwnProperty | Safe Object.hasOwn check with try-catch that handles null/undefined |
| i18n | getCommonTranslation(key) — retrieves localized strings from window.i18n |
| openingNewTab | Detects middle-click / Ctrl+Click / Cmd+Click to open URLs in new tabs |
| trim-separator | Cleans up context menu arrays by removing redundant separators and disabled items |
| use-click-outside | React hook for detecting clicks outside an element, useful for dropdowns and modals |
| uuid | UUID v4 generation utility for unique identifiers |
| add-log | Conditionally logs to the browser console or accumulates messages in window.logs based on ClientConfig settings |
| getTitleWithoutExtension | Strips the file extension from a title string, returning the bare name |
| image-helpers | Generates Maps of file extension → SVG icon URL for 24 / 32 / 64 / 96 px icon sizes across document and folder types |
| presentInArray | Checks whether a string exists in an array with optional case-insensitive comparison |
| socket | SocketHelper singleton for managing WebSocket connections, event subscriptions, and message emission with typed event/command enums |
| typeGuards | Type guard that checks whether a value is a Next.js StaticImageData object (src, height, width) |
Shared enums available from @onlyoffice/apps-ui-kit/enums:
| Enum | Values |
|---|---|
ShareAccessRights |
None, FullAccess, ReadOnly, DenyAccess, Varies, Review, Comment, FormFilling, CustomFilter, RoomManager, Editing, Collaborator |
EmployeeType |
Owner, Admin, RoomAdmin, User, Guest |
RoomsType |
PublicRoom, FormRoom, EditingRoom, VirtualDataRoom, CustomRoom, AIRoom |
EmployeeStatus |
Active, Disabled, Pending |
FileType |
Unknown, Archive, Video, Audio, Image, Spreadsheet, Presentation, Document, etc. |
FolderType |
DEFAULT, CommonDocuments, MyDocuments, Favorites, Recent, Trash, Archive, Rooms, etc. |
ThemeKeys |
BaseStr, DarkStr, SystemStr |
Shared constants from @onlyoffice/apps-ui-kit/constants:
| Constant | Description |
|---|---|
LOADER_STYLE |
Skeleton loading configuration (width, opacity, speed) |
OPERATIONS_NAME |
Operation type identifiers (trash, download, copy, move, convert) |
ROOM_ACTION_KEYS |
Room editing action keys |
EMPTY_ARRAY |
Frozen empty array for stable references |
EMPTY_OBJECT |
Frozen empty object for stable references |
FUNCTION_EMPTY |
No-op function |
LANGUAGE |
Cookie name constant: "asc_language" |
Key TypeScript types from @onlyoffice/apps-ui-kit/types:
| Type | Description |
|---|---|
TFile |
Comprehensive file object with security, status, and metadata |
TFolder |
Folder object with security and access rights |
TUser |
User/employee data including avatar, status, admin flags, quota |
TCreatedBy |
Author info: avatar, displayName, id, profileUrl |
TLogo |
Logo object with multiple sizes |
TFileSecurity |
Detailed file access rights |
TFolderSecurity |
Detailed folder access rights |
TRoomSecurity |
Detailed room access rights |
TViewAs |
View mode: "tile", "table", "row", "settings", "profile" |
| Variant | Item type | Description |
|---|---|---|
| Users Selector | TSelectorItemUser |
Picks workspace members; rows show avatar, role badge, employee type label, and group membership |
| Groups Selector | TSelectorItemGroup |
Picks user groups; distinguishes regular groups from system-managed ones |
| Files Selector | TSelectorItemFile |
Picks files; rows show file-type icon and extension label |
| Folders Selector | TSelectorItemFolder |
Picks folders; rows show folder icon with nested file and subfolder counts |
| Rooms Selector | TSelectorItemRoom |
Picks rooms; rows show room icon, cover image, tags, and room-type badge |
| MCP Selector | TSelectorItemMCP |
Picks Model Context Protocol agents; rows show agent icon and name |
The library ships with globalColors - 100+ named color tokens. It can be imported from the root barrel or from the theme provider entry point:
import { globalColors } from "@onlyoffice/apps-ui-kit";
// or
import { globalColors } from "@onlyoffice/apps-ui-kit/providers/theme";
// Base
const white = globalColors.white; // "#ffffff"
const black = globalColors.black; // "#333333"
const primary = globalColors.lightBlueMain;
const success = globalColors.mainGreen;
const error = globalColors.mainRed;
const warning = globalColors.mainOrange;Several components support automatic localization from window.i18n. This includes:
- Toast - automatic titles for success/error/warning/info notifications
- DropDownItem - beta and paid badge labels
- Selector - empty screen texts, button labels, and error messages
- Error pages - all error messages (Error401, Error403, Error404, etc.)
Set up window.i18n with your translations:
window.i18n = {
loaded: {
"en/Common.json": {
data: {
// Toast titles
Done: "Done",
Warning: "Warning",
Alert: "Alert",
Info: "Info",
// DropDownItem badges
BetaLabel: "Beta",
Paid: "Paid",
// Selector
ClearFilter: "Clear filter",
Back: "Back",
ContainsSpecCharacter: "Contains special characters",
NoRoomsFound: "No rooms found",
SelectorFormRoomEmptyScreenDescription: "Create a form filling room...",
SelectorVDREmptyScreenDescription: "Create a virtual data room...",
CreateFormFillingRoom: "Create form filling room",
CreateVirtualDataRoom: "Create virtual data room",
// FileInput
NotSupportedFormat: "Sorry, this file format isn't supported",
},
},
"de/Common.json": {
data: {
Done: "Fertig",
Warning: "Warnung",
Alert: "Achtung",
Info: "Info",
BetaLabel: "Beta",
Paid: "Kostenpflichtig",
ClearFilter: "Filter zurücksetzen",
Back: "Zurück",
// ... other translations
},
},
// Add more languages as needed
},
};Components read the language from the asc_language cookie. The language mapping:
en-US,en-GB→en- Other values are used as-is (e.g.,
ru,de,fr)
| Component | Translation Keys |
|---|---|
| Toast | Done, Warning, Alert, Info |
| DropDownItem | BetaLabel, Paid |
| Selector | ClearFilter, Back, ContainsSpecCharacter, NoRoomsFound, SelectorFormRoomEmptyScreenDescription, SelectorVDREmptyScreenDescription, CreateFormFillingRoom, CreateVirtualDataRoom |
You can import and use the getCommonTranslation utility directly:
import { getCommonTranslation } from "@onlyoffice/apps-ui-kit/utils";
const title = getCommonTranslation("Done"); // Returns localized "Done" or undefined
const label = getCommonTranslation("BetaLabel"); // Returns localized "BetaLabel" or undefinedThe Providers component integrates i18next for full internationalization support. This is the recommended approach for applications that need proper i18n with React hooks (useTranslation), automatic language detection, and the window.i18n bridge for standalone components.
- TranslationProvider initializes an i18next instance with your translations
- It wraps children in
I18nextProviderfromreact-i18next - It also sets
window.i18n.tandwindow.i18n.loadedso standalone components (Toast, DropDownItem, Selector, Error pages) can read translations without React context
Translations use a TTranslations type — a nested Map:
type TTranslations = Map<
string, // language code (e.g., "en", "ru", "fr")
Map<
string, // namespace (always "Common")
Record<string, string> // key-value translation pairs
>
>;The package ships English only -- Common.json, Payments.json and
Settings.json under @onlyoffice/apps-ui-kit/locales/en/. Only locales/en
is committed (see .gitignore), and pnpm pack ships what is on disk, so a
publish from a clean checkout carries English and nothing else:
import enCommon from "@onlyoffice/apps-ui-kit/locales/en/Common.json";Every other language comes from the host application. The portal apps build
their translations map from their own public/locales, and any consumer does
the same with its own resources; the kit's components read whatever
TranslationProvider is given. The 32 languages the portal carries can be
refreshed into a local checkout with pnpm sync-locales, which copies them
from a DocSpace-client checkout for Storybook and manual checks -- they are not
committed and not published.
import type { TTranslations } from "@onlyoffice/apps-ui-kit/providers/translation";
const translations: TTranslations = new Map([
["en", new Map([["Common", enCommon]])],
["ru", new Map([["Common", ruCommon]])],
["de", new Map([["Common", deCommon]])],
["fr", new Map([["Common", frCommon]])],
]);import { Providers } from "@onlyoffice/apps-ui-kit/providers";
function App() {
return (
<Providers
url="https://your-docspace.com"
apiKey="your-api-key"
translations={translations}
locale="en"
initialTheme="BaseStr"
errorFallback={(error) => <div>Error: {error.message}</div>}
onError={(error, errorInfo) => console.error(error, errorInfo)}
>
<YourApp />
</Providers>
);
}Inside the provider tree, use react-i18next hooks:
import { useTranslation } from "react-i18next";
function MyComponent() {
const { t } = useTranslation("Common");
return (
<div>
<h1>{t("Settings")}</h1>
<button>{t("SaveButton")}</button>
<button>{t("CancelButton")}</button>
</div>
);
}The TranslationProvider resolves the active language in this priority order:
localeprop (explicit override)user.cultureName(from user profile)settings.culture(from portal settings)"en"(fallback)
If settings and user are not passed as props, they are fetched automatically from the ONLYOFFICE Apps API using the url and apiKey provided.
| Prop | Type | Required | Description |
|---|---|---|---|
url |
string |
Yes | Base URL of the ONLYOFFICE Apps API |
apiKey |
string |
Yes | API key for authentication |
translations |
TTranslations |
No | Translation resources map |
locale |
string |
No | Locale override (e.g., "en", "ru") |
settings |
SettingsDto |
No | Portal settings (fetched from API if not provided) |
user |
EmployeeFullDto |
No | Current user (fetched from API if not provided) |
initialTheme |
ThemeKeys |
No | Initial theme: "BaseStr", "DarkStr", "SystemStr" |
systemTheme |
ThemeKeys |
No | Override for system theme detection |
colorTheme |
CustomColorThemesSettingsDto |
No | Color theme data |
errorFallback |
ReactNode | ((error: Error) => ReactNode) |
No | Custom error UI |
onError |
(error: Error, errorInfo: ErrorInfo) => void |
No | Error callback |
children |
ReactNode |
Yes | Child components |
You can also use sub-providers individually for more control:
import { ApiProvider } from "@onlyoffice/apps-ui-kit/providers/api";
import { TranslationProvider } from "@onlyoffice/apps-ui-kit/providers/translation";
import { ThemeProvider } from "@onlyoffice/apps-ui-kit/providers/theme";
import { ErrorBoundary } from "@onlyoffice/apps-ui-kit/providers/error-boundary";
<ErrorBoundary fallback={<ErrorPage />}>
<ApiProvider url="https://your-docspace.com" apiKey="your-api-key">
<TranslationProvider
translations={translations}
locale="en"
settings={settings}
user={user}
>
<ThemeProvider initialTheme="BaseStr" locale="en">
<App />
</ThemeProvider>
</TranslationProvider>
</ApiProvider>
</ErrorBoundary>;Inside ApiProvider, access API clients via the useApi hook:
import { useApi } from "@onlyoffice/apps-ui-kit/providers/api";
function MyComponent() {
const { profilesApi, commonSettingsApi } = useApi();
useEffect(() => {
profilesApi.getSelfProfile().then((res) => {
console.log(res.data.response);
});
}, [profilesApi]);
}This package is developed standalone — clone it and run pnpm install. No DocSpace-client
checkout is required for development, Storybook or tests; locales/en is committed so
everything works out of the box.
css/fonts.cssandfonts/are vendored here too, for the same reason: Storybook imports the stylesheet, so a fresh clone has to have it.pnpm sync-locales(withDOCSPACE_CLIENT_ROOTpointing at a DocSpace client checkout) refreshes them along with the non-English locales — run it when the upstream fonts or strings change.
The repository is LF everywhere, enforced by .gitattributes
(* text=auto eol=lf). Prettier is configured with endOfLine: "lf" and
pnpm format is part of the pre-push gate, so a CRLF working copy fails the
push — with every file in the repository listed, which says nothing about what
is actually wrong.
.gitattributes only decides what git writes at checkout. A clone made
after it landed is already correct and needs nothing. A checkout that predates
it keeps its CRLF working copies, so convert it once:
git status # commit or stash anything you care about first
git add --renormalize .
git checkout -- . # rewrites the working tree; discards uncommitted changesgit checkout -- . throws away uncommitted work — that is the point of the
git status above. git add --renormalize . on its own is not enough: it
updates the index, never the working tree.
To confirm afterwards, this prints nothing when the tree is clean — w/none is
an empty file, which has no line endings either way:
git ls-files --eol | grep -vE 'w/lf|w/none|i/-text'Open ui-kit.code-workspace rather than the folder. It adds six
grouped buttons to the status bar, each opening a list of tasks:
| Button | What it runs |
|---|---|
| 📖 Storybook | dev server, static build, serve the built site |
| ✅ Check | the whole pre-push gate in one go, or tsc / Biome / Vitest / Prettier on their own |
| 📦 Package | build, verify:package, both together, pack --dry-run, clean |
| 🔍 Audit | the checks nothing else runs — plugin API surface, drift against the plugin skill, undefined tokens and hardcoded hex, manifest invariants, missing docs — plus the component scaffolder |
| 📸 E2E | Playwright locally or in Docker, and the screenshot update |
| 🔄 Sync | pnpm install, and refreshing locales, fonts and icons from a client checkout |
The buttons come from the VsCodeTaskButtons extension
(spencerwmiles.vscode-task-buttons), which VS Code offers on first open — it is in
.vscode/extensions.json. Without it nothing is lost: the same
tasks are in Terminal → Run Task…, and every one of them is a pnpm script or a
node .claude/scripts/… command you can type yourself.
.vscode/launch.json adds debuggers for Vitest — the open file or
the whole suite — and a browser attached to a Storybook the Storybook button has already
started.
Adding a button means editing three files, and missing one leaves a button that appears and
does nothing: the script in package.json, the task in
.vscode/tasks.json, and the button in ui-kit.code-workspace, whose
task field must match the task's label exactly. .claude/rules/vscode-tasks.md has the
detail and a one-liner that checks the three agree.
pnpm storybookOpens at http://localhost:6006 - browse components, tweak props with the controls panel, and see live previews.
pnpm test # run all tests once
pnpm test:watch # watch mode
pnpm test:coverage # with coverage report
pnpm test:ui # Vitest UI dashboardThe specs in __tests__/ drive a Storybook on port 6007 and compare full-page
screenshots against the baselines committed next to them. They are not part of the
pre-push gate and not run in CI, so they only report what you ask them to.
pnpm test:e2e # locally: build Storybook, then run the specs
pnpm test:e2e:ui # the interactive Playwright runner
pnpm test:e2e:docker:build # build the image (once, and after a dependency change)
pnpm test:e2e:docker:start # run the suite inside it
pnpm test:e2e:docker:dev # same, with the sources mounted from the working tree
pnpm test:e2e:docker:update-screenshots # rewrite the baselines -- review every image
pnpm test:e2e:docker:clear # drop the imageCompare screenshots in Docker, not on the host. Font rendering and browser behaviour differ between macOS and Linux, so a baseline taken on the host fails everywhere else. The image carries the same fonts and the same pinned Chromium for everyone.
Both the report layout and the reporters match the DocSpace client's packages: dot for the
console, an HTML report and test-results.json, all under playwright-report/.
pnpm test:e2e:report # serve the last HTML report on port 9330WORKERS=1 pnpm test:e2e:docker:start pins the run to one worker, the way CI runs it —
worth doing when a spec fails only under parallel load.
pnpm build # production build (ESM + type declarations; no CJS)
pnpm build:watch # rebuild on file changesThe build also runs a set of checks against dist/: no bundled dependency compiled into
the output, every emitted module an index file (the exports map is one ./* wildcard,
and nothing else would resolve), and a "use client" directive preserved for each of the
55 modules that declare one — rollup drops module-level directives while bundling, and
without them every Next.js App Router consumer breaks on the first interactive component.
pnpm verify:package # pack, then run publint + attw against the real tarball
pnpm pack --dry-run # list exactly what would be publishedverify:package is what catches defects the lint/tsc/test gate cannot see — a broken
build, entry points missing because publishConfig was not applied, or types that
misrepresent the JavaScript. It must be run with pnpm: publishConfig field overrides
are a pnpm feature, and an npm-packed tarball has no exports/main at all.
pnpm lint # Biome - check for lint issues
pnpm lint:fix # Biome - auto-fix lint issues
pnpm format # Prettier - check formatting
pnpm format:fix # Prettier - rewrite filesLinting is Biome and formatting is Prettier: Biome's own formatter is disabled in
biome.json. Only lint, tsc and test run on pre-push and in CI, so formatting is
not enforced by any gate.
| Resource | URL |
|---|---|
| 🌐 ONLYOFFICE Website | onlyoffice.com |
| 🐙 DocSpace-client Monorepo | github.com/ONLYOFFICE/DocSpace-client |
| 📖 ONLYOFFICE API | api.onlyoffice.com |
| 💬 Community Forum | community.onlyoffice.com |
| 🆘 Help Center | helpcenter.onlyoffice.com |
| 📣 Feedback | feedback.onlyoffice.com |
Issues and feature requests are tracked in the DocSpace-client repository. Contributions are welcome - please open an issue or PR there, or start a discussion on the community forum.
Release notes are in CHANGELOG.md. Start there before upgrading: 4.0.0 renamed
the package, made it ESM-only and put an exports map in front of every subpath.
AGPL-3.0-only. See LICENSE.