All GuidesLast updated: April 2026
Migration Guide

Migrating from Elastic EUI to MUI, Chakra UI, or Ant Design — Complete Guide

Last updated: April 2026 · 12 min read

Why Teams Migrate Away from Elastic EUI

Elastic EUI (Elastic User Interface) is a well-crafted React component library originally built for Kibana, Elasticsearch dashboards, and other Elastic products. While it provides a polished set of data-dense components, many teams discover significant drawbacks when using EUI outside the Elastic ecosystem. Understanding these limitations helps you decide whether migration is worth the investment.

Tight coupling to the Elastic ecosystem.EUI was designed with Elastic products in mind. Its design language, component behavior, and even its color palette reflect Elastic’s brand identity. Teams building non-Elastic applications often find themselves fighting the library’s opinionated defaults rather than embracing them. Theming capabilities exist but are limited compared to general-purpose libraries like MUI or Chakra UI, where brand customization is a first-class concern.

Bundle size concerns.EUI ships approximately 188KB gzipped in a typical installation, which is notably larger than alternatives. MUI’s tree-shakeable architecture lets you import only the components you use, often resulting in 40-60% smaller production bundles. For teams targeting mobile-first audiences or optimizing Core Web Vitals, this difference in initial load time can directly impact user experience and search engine rankings.

Licensing and governance. EUI is released under the Elastic License 2.0 (ELv2), which is not an OSI-approved open source license. While it permits most use cases, the license includes restrictions on providing the software as a managed service. Legal teams at some organizations flag this as a compliance risk, especially for SaaS products. MUI (MIT), Chakra UI (MIT), and Ant Design (MIT) all use permissive licenses with no such restrictions.

Limited community outside Elastic. The EUI community is primarily composed of Elastic employees and contributors to the Elastic Stack. Stack Overflow questions, third-party tutorials, blog posts, and community plugins are significantly fewer compared to MUI (which has over 90,000 GitHub stars) or Ant Design (which dominates the enterprise UI space in Asia and increasingly worldwide). When your team encounters an edge case, finding community solutions for EUI is often harder than for mainstream alternatives.

Component Mapping: EUI to MUI

Below is a detailed mapping of the most commonly used Elastic EUI components and their Material UI equivalents. Each example shows the EUI source code on the left and the corresponding MUI code on the right. You can also use the FrontFamily Converter to automate these transformations instantly.

EuiButton → Button

Elastic EUI
<EuiButton fill color="primary">
  Save Changes
</EuiButton>

<EuiButton color="danger" isDisabled>
  Delete
</EuiButton>
Material UI
<Button variant="contained" color="primary">
  Save Changes
</Button>

<Button color="error" disabled>
  Delete
</Button>

EuiFieldText → TextField

Elastic EUI
<EuiFieldText
  placeholder="Enter your name"
  value={name}
  onChange={(e) => setName(e.target.value)}
  fullWidth
  isInvalid={!isValid}
/>
Material UI
<TextField
  placeholder="Enter your name"
  value={name}
  onChange={(e) => setName(e.target.value)}
  fullWidth
  error={!isValid}
  variant="outlined"
/>

EuiCard → Card

Elastic EUI
<EuiCard
  title="Analytics"
  description="View your data"
  icon={<EuiIcon type="dashboard" />}
/>
Material UI
<Card>
  <CardContent>
    <Typography variant="h6">Analytics</Typography>
    <Typography variant="body2">View your data</Typography>
  </CardContent>
</Card>

EuiText → Typography

Elastic EUI
<EuiText size="m">
  <h2>Section Title</h2>
  <p>Body content here.</p>
</EuiText>
<EuiTextColor color="subdued">
  Secondary text
</EuiTextColor>
Material UI
<Typography variant="h5">Section Title</Typography>
<Typography variant="body1">Body content here.</Typography>
<Typography color="text.secondary">
  Secondary text
</Typography>

EuiModal → Dialog

Elastic EUI
<EuiModal onClose={closeModal}>
  <EuiModalHeader>
    <EuiModalHeaderTitle>Confirm</EuiModalHeaderTitle>
  </EuiModalHeader>
  <EuiModalBody>Are you sure?</EuiModalBody>
  <EuiModalFooter>
    <EuiButton onClick={closeModal}>Cancel</EuiButton>
  </EuiModalFooter>
</EuiModal>
Material UI
<Dialog open={isOpen} onClose={closeModal}>
  <DialogTitle>Confirm</DialogTitle>
  <DialogContent>Are you sure?</DialogContent>
  <DialogActions>
    <Button onClick={closeModal}>Cancel</Button>
  </DialogActions>
</Dialog>

EuiCallOut → Alert

Elastic EUI
<EuiCallOut
  title="Update available"
  color="warning"
  iconType="alert"
>
  A new version is ready.
</EuiCallOut>
Material UI
<Alert severity="warning">
  <AlertTitle>Update available</AlertTitle>
  A new version is ready.
</Alert>

EuiBadge → Chip

Elastic EUI
<EuiBadge color="success">Active</EuiBadge>
<EuiBadge color="hollow">Draft</EuiBadge>
Material UI
<Chip label="Active" color="success" />
<Chip label="Draft" variant="outlined" />

Prop Conversion Reference

Beyond component name changes, migrating from EUI to MUI requires mapping EUI-specific props to their MUI equivalents. Here are the most common prop transformations you will encounter during a migration:

EUI PropMUI EquivalentNotes
fillvariant="contained"EUI boolean becomes MUI variant
isDisableddisabledBoolean prop rename
color="danger"color="error"Semantic color mapping
color="ghost"variant="text"Transparent background button
isInvaliderrorForm validation state
isLoadingloadingButton loading state
size="s"size="small"EUI abbreviates, MUI spells out
iconType="search"startIcon={<SearchIcon />}String icon ref to component
fullWidthfullWidthSame prop, same behavior

Which Framework Should You Migrate To?

The best migration target depends on your team’s priorities, application type, and design requirements. Here is how the three most popular alternatives compare for teams leaving EUI:

Material UI (MUI) — Best for Enterprise Applications

MUI is the most mature React component library with over 90,000 GitHub stars and a massive ecosystem. It offers a comprehensive set of components including DataGrid, DatePicker, and TreeView that are essential for data-heavy enterprise applications. If your EUI project involves dashboards, admin panels, or complex data tables, MUI provides the closest feature parity. The theming system is deeply customizable, and the MUI X suite offers advanced commercial components that match or exceed EUI’s data visualization capabilities.

Chakra UI — Best for Developer Experience

Chakra UI prioritizes developer ergonomics with its style props system, where layout and styling are expressed directly as component props. If your team values rapid prototyping, composable primitives, and a smaller learning curve, Chakra is an excellent choice. Its accessibility-first approach means every component ships with correct ARIA attributes, focus management, and keyboard navigation. However, Chakra lacks advanced data components like DataGrid, so teams with heavy tabular data requirements may need to pair it with a dedicated table library.

Ant Design — Best for Data-Dense Interfaces

Ant Design excels in enterprise scenarios with high information density. Its Table component, form system, and comprehensive set of data entry components make it a natural fit for teams migrating from EUI’s data-centric design philosophy. Ant Design also provides built-in internationalization for 50+ locales, a robust icon library, and a design system that scales well for large teams. The trade-off is a larger bundle size and a more opinionated visual style that can require significant effort to customize away from the default look.

Full Component Mapping Table

Below is a comprehensive mapping of EUI components to their equivalents across MUI, Chakra UI, and Ant Design. Use this table as a quick reference when planning your migration:

Elastic EUIMUIChakra UIAnt Design
EuiButtonButtonButtonButton
EuiFieldTextTextFieldInputInput
EuiCardCardBoxCard
EuiTextTypographyTextTypography
EuiAvatarAvatarAvatarAvatar
EuiBadgeChipBadge / TagTag
EuiModalDialogModalModal
EuiSwitchSwitchSwitchSwitch
EuiCallOutAlertAlertAlert
EuiSelectSelectSelectSelect
EuiLoadingSpinnerCircularProgressSpinnerSpin
EuiProgressLinearProgressProgressProgress
EuiToolTipTooltipTooltipTooltip
EuiBasicTableDataGridTable (manual)Table

Step-by-Step Migration Strategy

Migrating from Elastic EUI to another component library requires careful planning. Here is a proven strategy that minimizes risk and allows your team to deliver incrementally:

Step 1: Audit your EUI usage

Before writing any migration code, run a full inventory of every EUI component used in your codebase. Use a tool like grep -r "from '@elastic/eui'" to generate a complete import list. Group components by complexity: simple (Button, Badge, Avatar), medium (TextField, Select, Switch), and complex (BasicTable, Modal, DatePicker). This inventory becomes your migration backlog and helps you estimate the total effort accurately.

Step 2: Install the target library alongside EUI

Both libraries can coexist in the same project. Install your target library (MUI, Chakra, or Ant Design) and its peer dependencies without removing EUI. If migrating to MUI, install @mui/material, @emotion/react, and @emotion/styled. Verify that the CSS reset and global styles from both libraries do not conflict by rendering a test page with components from each.

Step 3: Create an abstraction layer

Build a thin wrapper component library that exports your own Button, Input, Card, etc. Initially these wrappers render EUI components internally. As you migrate, swap the internals to the new library without changing the public API. This pattern isolates consumers from the migration and lets you switch component-by-component without touching every file that uses a Button.

Step 4: Migrate in waves by complexity

Start with simple leaf components (Button, Badge, Avatar, Typography) in week one. Move to form components (TextField, Select, Checkbox, Switch) in week two. Tackle layout components (Card, Modal, Tabs, Accordion) in week three. Save data-heavy components (BasicTable, DataGrid, DatePicker) for last, as these require the most testing and often need custom configuration.

Step 5: Remove EUI and clean up

Once all components are migrated, remove @elastic/eui and its dependencies from your package.json. Run a final search for any remaining EUI imports. Audit your bundle size to confirm the expected reduction. Update your CI pipeline to flag any future EUI imports as lint errors to prevent regression.

What Developers Actually Hit

Based on migration reports from engineering teams. These are the problems documentation doesn’t warn you about.

⚠ EuiProvider is a monolith

EUI wraps theme, i18n, toast notifications, and global component defaults in a single EuiProvider. Extracting components one-by-one means either keeping EuiProvider alive for the remaining components (bloating the bundle with the entire EUI runtime) or breaking them all at once in a big-bang migration. There is no clean way to partially unwrap what EuiProvider provides.

⚠ EuiBasicTable pagination state management

EUI manages pagination internally within EuiBasicTable. MUI’s DataGrid requires explicit paginationModel state. Teams that relied on EUI’s “just pass items and it works” pattern discover they need to manage page index, page size, and total count themselves. What was zero state management becomes a useState + onPaginationModelChange handler on every table.

⚠ Color token mapping is lossy

EUI uses semantic color names (subdued, accent, success) that map to its own palette. MUI uses Material Design colors (primary, secondary, error). There is no 1:1 mapping — subdued has no MUI equivalent, and teams end up with a custom theme layer just to bridge the gap between the two color systems.

⚠ Date math dependency baggage

EUI depends on @elastic/datemath and moment. These are heavy dependencies that MUI doesn’t need. But if your app uses EuiSuperDatePicker’s relative date expressions ("now-15m", "now/d"), you need to keep these dependencies or completely rewrite the date parsing logic. There is no MUI component that understands Elastic’s relative date syntax.

Convert EUI to MUI instantly

Skip the manual work. Paste your Elastic EUI code into the FrontFamily Converter and get production-ready MUI output with correct imports, prop mappings, and component restructuring. A pre-loaded EuiCard example is ready for you to try.

Open Converter with EUI → MUI Example

Common Pitfalls When Migrating from EUI

EUI’s global CSS reset.EUI injects a comprehensive CSS reset and global styles that affect typography, spacing, and element defaults across your entire application. When you remove EUI, elements that relied on these global styles may shift unexpectedly. Before removing EUI, audit which global styles your application depends on and replicate them in your own stylesheet or in the target library’s theme configuration.

Icon system differences. EUI uses a string-based icon system where icons are referenced by name (iconType="search"). MUI, Chakra, and Ant Design all use component-based icons. You will need to create a mapping from EUI icon names to the equivalent icon components in your target library. Consider using lucide-react or react-icons as a universal icon solution that works across all frameworks.

EuiProvider and service dependencies. EUI provides services through its EuiProvider context including internationalization, color mode, and component defaults. Components that rely on these services will break when extracted from the provider tree. Identify all provider-dependent features early and plan equivalent solutions in your target library.

TypeScript Migration Patterns

Migrating components is only half the battle. EUI's type system is heavily reliant on complex generics and custom utility types. These are the patterns that consume most of a senior engineer's migration time.

A. Generic Type Mapping (Data Tables)

EUI uses generics to enforce type safety on row items. When moving to MUI's DataGrid, you must map these generic definitions — the column type names and prop shapes differ significantly.

EUI (Before)
import type { EuiBasicTableColumn } from '@elastic/eui';

interface User {
  id: string;
  name: string;
  role: string;
  status: 'active' | 'inactive';
}

const columns: Array<EuiBasicTableColumn<User>> = [
  { field: 'name', name: 'Full Name', sortable: true },
  { field: 'role', name: 'Role' },
  {
    field: 'status',
    name: 'Status',
    render: (status: User['status']) => (
      <EuiHealth color={status === 'active' ? 'success' : 'danger'}>
        {status}
      </EuiHealth>
    ),
  },
];
MUI (After)
import type { GridColDef } from '@mui/x-data-grid';

interface User {
  id: string;
  name: string;
  role: string;
  status: 'active' | 'inactive';
}

const columns: GridColDef<User>[] = [
  { field: 'name', headerName: 'Full Name', sortable: true },
  { field: 'role', headerName: 'Role' },
  {
    field: 'status',
    headerName: 'Status',
    renderCell: ({ row }) => (
      <Chip
        color={row.status === 'active' ? 'success' : 'error'}
        label={row.status}
        size="small"
      />
    ),
  },
];
Key differences
  • 1. EuiBasicTableColumn<T>GridColDef<T> — different generic wrapper
  • 2. nameheaderName — column display name
  • 3. render: (value: T[K]) =>renderCell: ({ row }) => — render function receives params object, not raw value
  • 4. MUI DataGrid requires @mui/x-data-grid as a separate package (not in core)

B. Dealing with ExclusiveUnion

Standard TypeScript unions (A | B) allow properties from both types to coexist. EUI uses a custom ExclusiveUnion utility to strictly forbid mixing props — for example, preventing an EuiButton from accepting both onClick (button behavior) and href (anchor behavior) simultaneously.

MUI handles this differently using the component prop and OverridableComponent utility type.

EUI — ExclusiveUnion prevents mixing
// ✅ Valid — button behavior only
<EuiButton onClick={handleClick}>Save</EuiButton>

// ✅ Valid — anchor behavior only
<EuiButton href="/login">Login</EuiButton>

// ❌ TypeScript Error!
// ExclusiveUnion prevents both onClick AND href
<EuiButton href="/login" onClick={handleClick}>
  Login
</EuiButton>
MUI — component prop polymorphism
// ✅ Default — renders as <button>
<Button onClick={handleClick}>Save</Button>

// ✅ As anchor — renders as <a>
<Button component="a" href="/login">Login</Button>

// ✅ As Next.js Link
<Button component={Link} href="/login">
  Login
</Button>

// TypeScript knows which props are valid
// based on the component prop value
Migration pattern for wrapper components

If you have wrapper components that forward EUI's ExclusiveUnion types, rewrite them using MUI's typed component pattern:

// Before: EUI wrapper with ExclusiveUnion
type Props = EuiButtonProps; // includes ExclusiveUnion internally

// After: MUI wrapper with generic component type
import type { ButtonProps } from '@mui/material/Button';

// For button usage:
type Props = ButtonProps<'button'>;

// For anchor usage:
type Props = ButtonProps<'a', { href: string }>;

// For polymorphic usage:
type Props<C extends React.ElementType = 'button'> = ButtonProps<C>;

C. EUI-Specific Type Utilities You'll Lose

EUI TypePurposeMUI Equivalent
ExclusiveUnion<A, B>Strict either/or propsUse OverridableComponent or discriminated unions
EuiBasicTableColumn<T>Typed table columnsGridColDef<T>
EuiComboBoxOptionOption<T>Typed combobox optionsUse AutocompleteProps<T, ...> — 4 generic params
PropertySortSort configurationGridSortModel
PaginationPagination stateGridPaginationModel
EuiSelectableOption<T>Typed selectable itemsNo direct equivalent — use custom interface with MUI Select

Interactive Component Reference

Search any EUI component to find its MUI equivalent. Components marked with ⚠ have no direct replacement and require custom implementation.

48 / 48
Elastic EUIMaterial UIProps / Notes
EuiButton
fill, color, isDisabled
Button
variant="contained", color, disabled
fill→variant="contained", no fill→variant="outlined"
EuiButtonEmpty
color, size
Button
variant="text"
EUI has a separate component; MUI uses variant prop
EuiButtonIcon
iconType, color
IconButton
color
iconType→pass icon as children
EuiFieldText
placeholder, value, isInvalid
TextField
placeholder, value, error
isInvalid→error (boolean)
EuiFieldPassword
type, value
TextField
type="password", value
EUI has dedicated component; MUI uses type prop
EuiFieldNumber
value, min, max
TextField
type="number", value, inputProps={{ min, max }}
Number constraints go in inputProps
EuiTextArea
value, rows
TextField
multiline, value, rows
Add multiline prop
EuiFieldSearch
value, onChange
TextField
value, onChange, InputProps={{ startAdornment }}
Add search icon via InputAdornment
EuiCard
title, description, hasShadow
Card + CardContent
elevation, + Typography inside
MUI Card needs sub-components for structure
EuiPanel
paddingSize, hasShadow
Paper
elevation, sx={{ p }}
EuiPanel is a generic container; Paper is closest
EuiText
size, color="subdued"
Typography
variant, color="textSecondary"
size "xs"→"caption", "s"→"body2", "m"→"body1"
EuiTitle
size="l"
Typography
variant="h4"
size "l"→"h4", "m"→"h5", "s"→"h6"
EuiAvatar
name, imageUrl, size
Avatar
alt, src, sx={{ width, height }}
imageUrl→src, size needs sx override
EuiBadge
color, iconType
Chip
color, icon
EuiBadge is simpler; Chip has more variants
EuiModal
onClose
Dialog
onClose, open
Needs separate open state; EUI handles internally
EuiModalHeaderDialogTitleSub-component mapping
EuiModalBodyDialogContentSub-component mapping
EuiModalFooterDialogActionsSub-component mapping
EuiConfirmModal
title, onConfirm, onCancel
Dialog + custom
open, onClose + buttons
No direct equivalent; build with Dialog + DialogActions
EuiSwitch
checked, onChange, label
Switch + FormControlLabel
checked, onChange, label
MUI Switch needs FormControlLabel wrapper for label
EuiCheckbox
checked, onChange, label, id
Checkbox + FormControlLabel
checked, onChange, label
Same wrapper pattern as Switch
EuiCallOut
title, color, iconType
Alert
severity, icon
color "danger"→severity="error", "success"→"success"
EuiSelect
options, value, onChange
Select + MenuItem
value, onChange + children
MUI Select uses MenuItem children instead of options array
EuiSuperSelect
options, valueOfSelected
Select + MenuItem
value, renderValue
SuperSelect rich options need custom MenuItem rendering
EuiComboBox
options, selectedOptions, onChange
Autocomplete
options, value, onChange, renderInput
Complex mapping — needs renderInput prop
EuiLoadingSpinner
size
CircularProgress
size
size "l"→40, "m"→24, "s"→16
EuiProgress
value, max, size
LinearProgress
value, variant="determinate"
value is 0-100 in MUI (percentage), not raw value
EuiToolTip
content, position
Tooltip
title, placement
content→title, position→placement
EuiHorizontalRuleDividerDirect replacement
EuiTabs
children EuiTab
Tabs + Tab
value, onChange
MUI Tabs are controlled; EUI allows uncontrolled
EuiBasicTable
items, columns, pagination
DataGrid
rows, columns, paginationModel
Significant API difference — MUI DataGrid is @mui/x-data-grid
EuiInMemoryTable
items, columns, search
DataGrid + custom filterBuilt-in search needs custom toolbar in MUI
EuiFlyout
onClose, side
Drawer
onClose, anchor
side→anchor ("right"→"right")
EuiPopover
button, isOpen, closePopover
Popover
anchorEl, open, onClose
MUI uses anchorEl ref pattern instead of button prop
EuiContextMenu
panels
Menu + MenuItem
anchorEl, open
EUI panel-based navigation has no direct MUI equivalent
EuiSpacer
size
Box
sx={{ my }}
Use Box with margin or Stack spacing
EuiFlexGroup
gutterSize, direction
Stack or Box
spacing, direction
MUI Stack is the closest equivalent
EuiFlexItem
grow
Box
sx={{ flexGrow }}
No dedicated component in MUI
EuiAccordion
buttonContent, isLoading
Accordion + AccordionSummary
expanded, expandIcon
MUI needs sub-components
EuiDatePicker
selected, onChange
DatePicker (@mui/x-date-pickers)
value, onChange
Requires @mui/x-date-pickers package
EuiSuperDatePicker
start, end, onTimeChange
No direct equivalentNo MUI equivalent — build custom with two DatePickers + presets
EuiGlobalToastList
toasts, dismissToast
No direct equivalentNo direct equivalent — use notistack or build custom Snackbar manager
EuiHealth
color
No direct equivalentSimple colored dot — build with Box sx={{ width: 8, height: 8, borderRadius: "50%" }}
EuiStat
title, description
No direct equivalentNo MUI equivalent — build custom with Typography + Box
EuiColorPicker
color, onChange
No direct equivalentNo MUI equivalent — use react-colorful or similar
EuiMarkdownEditor
value, onChange
No direct equivalentNo MUI equivalent — use @uiw/react-md-editor or similar
EuiCodeBlock
language, children
No direct equivalentNo MUI equivalent — use react-syntax-highlighter or prism-react-renderer
EuiProvider
colorMode
ThemeProvider + CssBaseline
theme
EUI provider wraps theme + globals; MUI separates them

Import Changes at a Glance

Elastic EUI
import {
  EuiButton,
  EuiFieldText,
  EuiCard,
  EuiText,
  EuiModal,
  EuiCallOut,
  EuiBadge,
} from '@elastic/eui';
Material UI
import Button from '@mui/material/Button';
import TextField from '@mui/material/TextField';
import { Card, CardContent } from '@mui/material';
import Typography from '@mui/material/Typography';
import Dialog from '@mui/material/Dialog';
import Alert from '@mui/material/Alert';
import Chip from '@mui/material/Chip';