Examples › RTL support

Mantine ContextMenu fully supports RTL (right-to-left) layouts, making it suitable for Arabic, Hebrew, Persian, and other RTL languages. The component automatically detects and adapts to the text direction when wrapped in Mantine's DirectionProvider.

How RTL support works

When using RTL mode, the context menu automatically adjusts:

  • Menu positioning — The menu appears to the left of the cursor instead of the right, ensuring it stays within viewport bounds
  • Text alignment — Text and content align to the right, following RTL reading direction
  • Icon spacing — Icons are positioned on the correct side using logical CSS properties (margin-inline-start and margin-inline-end)
  • Submenu positioning — Submenus open to the left of their parent items, with properly positioned indicators

Setting up RTL

To enable RTL support, wrap your application (or the relevant section) in Mantine's DirectionProvider component. The context menu will automatically detect the direction using Mantine's useDirection hook:

'use client';

import { DirectionProvider, Group, Paper, Switch, Text } from '@mantine/core';
import { ContextMenuProvider } from 'mantine-contextmenu';
import { useState } from 'react';

export function RtlProviderExample({ children }: { children: React.ReactNode }) {
  const [direction, setDirection] = useState<'ltr' | 'rtl'>('ltr');

  return (
    <>
      <Paper p="md" mb="md" withBorder>
        <Group justify="space-between" align="center">
          <div>
            <Text fw={500} size="sm">
              Direction: {direction.toUpperCase()}
            </Text>
            <Text size="xs" c="dimmed">
              Toggle to test RTL support
            </Text>
          </div>
          <Switch
            checked={direction === 'rtl'}
            onChange={(event) => setDirection(event.currentTarget.checked ? 'rtl' : 'ltr')}
            label="RTL Mode"
            size="md"
          />
        </Group>
      </Paper>

      <DirectionProvider key={direction} initialDirection={direction}>
        <ContextMenuProvider repositionOnRepeat>
          <div style={{ direction }}>{children}</div>
        </ContextMenuProvider>
      </DirectionProvider>
    </>
  );
}

The DirectionProvider (see Mantine DirectionProvider docs) sets the text direction for all Mantine components within its context. The context menu automatically reads this direction and adjusts its behavior accordingly.

Interactive example

Toggle the switch below to test RTL support. Try right-clicking at different positions (left, center, right edges of the image) to see how the menu intelligently positions itself. Notice how submenus open in the correct direction and all text, icons, and indicators are properly aligned:

Direction: LTR

Toggle to test RTL support

Picture by Daria Shatova | Mantine ContextMenu

Picture by Daria Shatova

Here's the code for the above example:

'use client';

import {
  IconClipboard,
  IconCopy,
  IconCut,
  IconDownload,
  IconEdit,
  IconFileExport,
  IconShare,
  IconTrash,
} from '@tabler/icons-react';
import { useContextMenu } from 'mantine-contextmenu';
import { Picture } from '~/components/Picture';
import { unsplashImages } from '~/lib/images';

export function RtlSupportExampleContent() {
  const { showContextMenu } = useContextMenu();
  const image = unsplashImages[0];

  return (
    <Picture
      image={image}
      onContextMenu={showContextMenu([
        {
          key: 'edit',
          icon: <IconEdit size={16} />,
          title: 'Edit',
          onClick: () => {},
        },
        {
          key: 'copy',
          icon: <IconCopy size={16} />,
          title: 'Copy',
          onClick: () => {},
        },
        {
          key: 'cut',
          icon: <IconCut size={16} />,
          title: 'Cut',
          onClick: () => {},
        },
        {
          key: 'paste',
          icon: <IconClipboard size={16} />,
          title: 'Paste',
          onClick: () => {},
        },
        { key: 'divider-1' },
        {
          key: 'share',
          icon: <IconShare size={16} />,
          title: 'Share',
          items: [
            {
              key: 'share-email',
              title: 'Email',
              onClick: () => {},
            },
            {
              key: 'share-social',
              title: 'Social Media',
              items: [
                { key: 'facebook', title: 'Facebook', onClick: () => {} },
                { key: 'twitter', title: 'Twitter', onClick: () => {} },
                { key: 'linkedin', title: 'LinkedIn', onClick: () => {} },
              ],
            },
            {
              key: 'share-link',
              title: 'Copy Link',
              iconRight: <IconCopy size={16} />,
              onClick: () => {},
            },
          ],
        },
        {
          key: 'export',
          icon: <IconFileExport size={16} />,
          title: 'Export',
          items: [
            { key: 'export-pdf', title: 'Export as PDF', onClick: () => {} },
            { key: 'export-png', title: 'Export as PNG', onClick: () => {} },
            { key: 'export-svg', title: 'Export as SVG', onClick: () => {} },
          ],
        },
        { key: 'divider-2' },
        {
          key: 'download',
          icon: <IconDownload size={16} />,
          title: 'Download',
          onClick: () => {},
        },
        {
          key: 'delete',
          icon: <IconTrash size={16} />,
          title: 'Delete',
          color: 'red',
          onClick: () => {},
        },
      ])}
    />
  );
}