Panel Material UI Announcement

announcement
panel
Announcing the release of panel-material-ui, a new extension that wraps Material UI components in Panel.
Author

Philipp Rudiger

Published

July 11, 2025

Today we are excited to introduce panel-material-ui, a Panel extension that delivers a suite of modern components and a cohesive design system built on Material UI. It is fully compatible with your existing Panel code.

We are beginning to use it in production, so you can expect this extension to mature rapidly. Our goal is to replace the existing Panel components with panel-material-ui components with the release of Panel 2.0.

We also welcome feedback and community contributions. In particular, we are seeking testers to systematically review each component and the reference guide. Please join us on GitHub.

Try it out today with:

pip install panel-material-ui

or:

conda install conda-forge::panel-material-ui

Why?

We have been building Panel for almost seven years. Today, it powers interactive dashboards, visualizations, AI workflows, and data applications in R&D, universities, start-ups and Fortune 500 companies, with over 1.2 million downloads per month. While users love the power and versatility of Panel, one piece of feedback we have received repeatedly is that it is too difficult to style Panel components.

So, we set out to build a comprehensive, consistent, and API-compatible set of components based on Material UI. We re-implemented existing Panel components and added new ones, exposing as many MUI components as possible, while ensuring compatibility with existing Panel components.

Why MUI in particular? We wanted to build on a library that offers:

  • Maturity: MUI is one of the most widely used React component libraries, with strong community support and robust documentation.
  • Consistent Design: It implements Google’s Material Design system, providing a polished and consistent user experience out of the box.
  • Customizable and themeable: MUI has a well-designed theming system that maps cleanly to how we wanted to style Panel apps.
  • Strong accessibility support: MUI handles many of the hard parts of accessibility for us, giving us a more solid foundation.
  • Component breadth: It includes a wide range of components — from simple buttons to complex layout and navigation elements — that we can gradually wrap and expose in Panel.

For our (and your) sanity, we also wanted a library that would make it easy to create new components. The robust React implementation made it a joy to build, especially with AI assistance. This also means creating custom components based on panel-material-ui is a breeze.

How?

With the addition of ESM components, it has finally become straightforward to build custom Panel components. Building on the React integration, I quickly discovered that mapping parameters in Python to React state was a simple problem where LLMs could provide significant assistance. Using a simple prompt, I was able to bootstrap a comprehensive set of components and their respective React implementations. That said, I’ve always been skeptical of fully auto-generated component wrappers. While promising in theory, the user experience of a purely automated wrapper library inevitably suffers, and the initial AI-generated component set was no exception.

Once the basic structure was in place, we methodically reviewed, refined, and documented each component in detail. At this point, I’d like to extend special thanks to Thuy Do and Marc Skov Madsen for their invaluable contributions in writing tests, creating documentation, and providing feedback throughout the process.

What can it do?

Okay, so what can it actually do? Yes, we’ve added 70+ components, and they’re all consistently styled, but what else is possible now that wasn’t before?

Theming & Styling

Previously, Panel introduced a so-called Design system. We build on that here, but primarily use it to ensure that the theme defined in panel-material-ui flows down to standard Panel components. Let’s briefly define our terminology:

  • Design: The overall set of CSS stylesheets and component defaults that align an application with a particular design system.
  • Theme: A theme in Panel defines the configuration of default color settings in both light (default) and dark mode.
  • Styling: Applying one-off modifications to specific components.

In panel-material-ui, the main entry point for theming is the theme_config parameter, available on all Material components. It makes it easy to configure default colors, typography, and more—either globally or differently for dark and light themes. Thanks to inheritance, the theme_config can be applied to a parent component and will flow down to all its children:

from panel_material_ui import Card, Button, FloatSlider

Card(
    FloatSlider(label="Child Slider", color="primary"),
    Button(label="Child Button", color="primary"),
    title="Parent Card",
    theme_config={
        "palette": {
            "primary": {"main": "#d219c9"},
        },
        "typography": {
            "fontFamily": "monospace"
        },
        "shape": {
            "borderRadius": 12
        }
    },
    margin=10
)

This approach allows controlling the theme across an entire application or, thanks to inheritance, overriding the theme for some part of an application, e.g. to apply custom styles in the header or sidebar.

For one-off customizations the sx parameter provides fine-grained control over every aspect of an component:

FloatSlider(
    label="My Styled Slider",
    margin=20,
    sx={
        ".MuiFormLabel-root": {"fontFamily": "monospace"},
        ".MuiSlider-thumb": {
            "borderRadius": 0  # square
        },
        ".MuiSlider-thumb:hover": {
            "background-color": "deeppink"
        }
    }
)

Branding

Using the above theming and styling, and the Page element, it is now simple to create branded apps:

branding-example

Here is another example of a highly customized dashboard:

custom-dashboard

Dark Mode

In Panel theming happened on the backend, i.e. if you toggled between the default (light) and the dark theme the application would have to re-run in order to ensure the updated theming would be propagated. In panel-material-ui theming happens client-side. By using the ThemeToggle or Page components in your app you enter managed theming mode where the theme is controlled globally and styles are updated dynamically.

toggle = pmui.ThemeToggle(styles={'margin-left': 'auto'})
plot = df.hvplot.scatter(
    'bill_length_mm', 'bill_depth_mm', c='species', responsive=True, min_height=300
)
plotly_plot = px.scatter(
    df, x='bill_length_mm', y='bill_depth_mm', color='species'
)
plots = pmui.Tabs(('Bokeh', plot), ('Plotly', plotly_plot))
table = pn.widgets.Tabulator(df.iloc[:, :5], page_size=5, sizing_mode='stretch_width')

pmui.Paper(toggle, plots, table, elevation=2)

Responsiveness

While Panel could be configured to provide a good mobile experience, it was previously quite cumbersome. In panel-material-ui we are paying attention to the mobile experience:

  • the Page dynamically responds to smaller screens by switching the sidebar
  • the Grid component allows configuring the layout per breakpoint
  • the BreakpointSwitcher allows dynamically switching between different components depending on the breakpoints.

Try resizing the output:

from panel_material_ui import Grid

item = lambda content: pmui.Paper(
    content, elevation=5, height=100, sizing_mode="stretch_width"
)

small = pmui.Select(options=pmui.COLORS)
large = pmui.RadioButtonGroup(options=pmui.COLORS)

switcher = pmui.BreakpointSwitcher(
    media_query='(min-width: 800px)',
    small=small,
    large=large,
)

grid = Grid(
    Grid(item('Foo'), size={"sm": 8, "xs": 12}),
    Grid(item('Bar'), size={"sm": 4, "xs": 12}),
    Grid(item('Baz'), size={"sm": 4, "xs": 12}),
    Grid(item('Qux'), size={"sm": 8, "xs": 12}),
    container=True, margin=10, spacing=2
)

pmui.Column(switcher, grid)

Note how the switcher toggles between large and small implementations and the grid adjusts to the configured breakpoints.

Compatibility

Above, we mentioned that panel-material-ui has full compatibility with Panel, but what does that actually mean? The goal from the very beginning was to provide a set of components that could be drop-in replacements for existing Panel components.

This means that you should be able to change the imports and, with a few minor exceptions, the panel-material-ui component should behave just like the original Panel one. For example change

import panel as pn

pn.widgets.Button(name='Click me', button_type='primary')

to

import panel_material_ui as pmui

pmui.widgets.Button(name='Click me', button_type='primary')

The idea is that the transition to panel-material-ui should be as easy as possible, and when we eventually replace the original Panel components as part of the Panel 2.0 release, there will not be a major transition either. Compatibility also means you can mix and match regular panel and panel-material-ui components as you see fit.

What’s next?

With this being an early release, our work of course isn’t done. Creating, polishing, documenting, and testing 70+ components is a significant effort, even with AI assistance. Over the past two months we have been polishing components one-by-one and are now at a point where we have confidence that you’ll have a good experience and will appreciate the improvements and power it brings. In our work we have already started to use the library in production and are seeing great results. Thanks to the simplicity of the approach, resolving issues will be much simpler than the complex build chain required to develop the original components in Bokeh and Panel.

As we incubate these components in the panel-material-ui library, we aim to reach stability and maturity in the coming months and eventually replace the existing components in Panel with this more modern implementation as part of the Panel 2.0 release by the end of the year.

For now, we would love for you to try it out, share your feedback, and show us on X, LinkedIn or Discourse what you build with it!

Back to top