Skip to main content
react-md
react-md - Blog - v2 Release

v2 Release

The v2 release is a complete re-write of react-md to address the majority of problems encountered while using v1. Unfortunately, this took a lot longer than I had hoped since I ended up using this project to learn Typescript as well as the new React hooks API. Even though there are some missing components from v1, I think the new functionality outweighs it and the components are scoped for a later release.

Missing Components and Functionality from v1

Before starting the migration to v2, I highly recommend reviewing the [Working with v1] guide and reviewing the following missing functionality:

  • dropped full support for IE 11 and mostly targeting evergreen browsers for the new dynamic themes
  • some styling props have been removed since the new component API should hopefully eliminate the need for those props. (These props were normally get*Style, get*ClassName, etc)
  • controlling the visibility and value of some components has been removed since I struggled figuring out a good way to handle controlled/uncontrolled components and hooks. I definitely plan on adding support for this functionality back in.
  • the BottomNavigation component has not been added to the initial v2 release. This component might be added at a later time, but the new @react-md/app-bar package should be able to solve a decent amount of use cases.
  • the DatePicker and TimePicker components have not been added to the initial v2 release. These two components will definitely be added in a later release, but require a lot more time to implement since dates are hard and I want to create all the sub-components in a reusable and exportable way for additional customization.
  • removed the FileUpload component since it didn't feel extremely useful since it didn't actually upload a file to a server
  • the mini variants for the Drawer/NavigationDrawer components have not yet been implemented in the new Layout component.
  • removed the MenuTab component due to never really working correctly and the new @react-md/tabs package should allow for a much better user experience and customization.
  • removed the ListItemControl component since it was implemented pretty badly. The new @react-md/list and @react-md/form packages should allow for this to be re-implemented in your app fairly easily though.
  • removed the TablePagination and TableCardHeader components since I wasn't sure how helpful they were and didn't really work. The new @react-md/table styling behavior and customization should allow for a custom implementation fairly easily though.
  • removed the EditDialogColumn, SelectFieldColumn, *Column components from the @react-md/table package since they aren't really needed anymore. Form elements should be renderable within tables without the weird hacks these components were implementing.
  • removed the Version component since it was probably only ever used by the documentation site.

What's new in v2?

This release focused on updating react-md to be as customizable as possible by exporting a lot more low-level components, hooks, and utils that were used internally. Something that I noticed from the v1 release is that if something was not easily customizable or specific components were not public, it was easier to actually write your own version of that component. This was a major flaw and caused lots of problems especially since the majority of the functionality behind these components were not reusable. The new components and hooks should be able to help for these use cases but the down side is that you'll still need to create a custom implementation for common patterns throughout your app because some patterns seem repetitive.

Rewrite to Typescript

Note: Upgrading to v2 of react-md does not require you to also use Typescript even though it is highly recommended to do so. You can still use react-md with JavaScript as you have done before, but all the examples and documentation will be written with Typescript.

v1.1.0 was the first react-md release to add support for Typescript but unfortunately the type definitions were not that great because I did not fully understand Typescript and kept getting out of sync or forgotten. Since Typescript is becoming the new trend for web development with additional tooling and editor integrations, it seemed like a good time to finally start learning Typescript and re-write react-md to use it natively.

One of the biggest pros for rewriting to Typescript is that now all the documentation around Component props, hooks, or utils can now be viewed within your editor or IDE if it supports the "Go to Definition" functionality due to being compiled with the *.d.ts files now. In addition, the type definitions should never be out of sync and should be much more strictly typed than before since the entire library has been written in Typescript.

That being said, there are a few type definitions that could still be improved in v2 especially around generics and Records so any suggestions or PRs are welcomed.

New Behavior for Determining the Current Application Size

In v1, the Drawer and NavigationDrawer components were used to be able to determine the current app size using the static methods: getCurrentMedia and matchesMedia. This meant that the only way to determine the current application size through media queries was to either:

  • render a Drawer or NavigationDrawer components and hook into the onMediaTypeChange callback prop
  • manually create a resize listener and use the Drawer.getCurrentMedia/NavigationDrawer.getCurrentMedia static methods
  • implement a custom solution

This solution was pretty terrible and lead to confusion, out-of-sync behavior, and bugs.

Starting with v2 these issues should be resolved due to React implementing the new Context API along with the new AppSizeListener component and media query SCSS mixins.

AppSizeListener Component and useAppSize Hook

The new AppSizeListener component should be mounted once near the root of your app which will initialize multiple resize and media query event listeners to determine what size your app is currently being rendered while the useAppSize hook allows access to the current size.

Check out the AppSizeListener example for more details and a live demo.

Media Query Mixins and Components

In addition to the new component and hook, react-md now provides a few utility SCSS mixins for applying styles at different media query sizes:

There are also a few media query components which can be used to conditionally render components based on the current application site. Check out the Media Query Components example for some more information.

New Theme API

Since this release dropped IE 11 support, all the dynamic theming is done through Custom CSS Properties (CSS Variables) with the new theme SCSS functions and mixins. The root @react-md/theme package now provides a new background and surface color variable to use along with a lot of other themeable values while the other packages will provide themeable variables for colors, margin, and padding. The new theme API allows you to easily modify existing themes without all the boilerplate and "hacks" from the v1 release. Here is a quick example:

v1 theme updates
.custom-theme--v1 {
  // create smaller icon buttons
  .md-btn--icon {
    height: 1rem;
    font-size: 1rem;
    width: 1rem;
  }

  // updating icon spacing
  .md-icon-text {
    $new-spacing: 0.5rem;

    &:first-child {
      padding-right: $new-spacing;
    }

    &:last-child {
      padding-left: $new-spacing;
    }
  }

  // change theme colors
  @include react-md-theme-colors($md-orange-500, $md-purple-a-200);
}
v2 theme updates
// in v2
.custom-theme--v2 {
  @include rmd-button-theme-update-var(icon-size, 1rem);
  @include rmd-icon-theme-update-var(text-spacing, 0.5rem);
  @include rmd-theme-update-var(primary, $rmd-orange-500);
  @include rmd-theme-update-var(secondary, $rmd-purple-a-200);
}

You can also reference the current theme values as a value or CSS Variable:

.custom-class {
  // use current background-color
  @include rmd-theme(background-color, background);

  // use the current primary text color on the current background color
  @include rmd-theme(color, text-primary-on-background);

  // use the current primary theme color as a border color (for some reason)
  @include rmd-theme(border-color, primary): ;

  // add 1rem to the current CSS Variable value for the icon text spacing
  padding: calc(#{rmd-icon-theme-var(text-spacing)} + 1rem);
}

The new theme API is pretty powerful and allows for a lot of new customization without needing to modify the react-md internals and remembering specific class names. Please check out the creating dynamic themes documentation for more information.

Disclaimer: This new theme API was heavily inspired by the material components web library.

New Utility SCSS Functions and Mixins

This release added a bunch of new utility functions and mixins to add additional styling throughout your app for common use cases such as:

Check out the SassDoc pages for additional documentation and examples for common mixins:

Automatic Color Fixes for Accessible Contrast Ratios

The default styles from react-md will now attempt to automatically fix contrast ratio issues between foreground and background colors using the new rmd-theme-tone function. This appears to work for a great amount of use cases, but it is still recommended to verify colors meet the recommended contrast ratio for accessibility. You can check out the Theme Builder for a few examples of contrast ratio issues by playing with the theme colors.

SCSS Variables and Default Values in JavaScript

Every SCSS variable is now automatically compiled with its default value and written to a dist/scssVariables.js file for each package as a record of variable names as keys and the default compiled value as the value. This allows you to get access to each variable name as well as all the themeable CSS Variables in JavaScript.

For example, if you want to get all the available material design colors from the theme package, you can use the following code:

import scssVariables from "@react-md/theme/dist/scssVariables";

// get all the colors from the color palette
// only the color variables in this package will not be prefixed
// with rmd-theme
const colorKeys = Object.keys(scssVariables).filter(
  (name) => !/^rmd-theme/.test(name)
);

Note: This will be strictly typed for Typescript users as well.

Improved Typography and CSS Reset

To be honest, I had no idea what I was doing with typography in v1 (I still don't really understand typography) and defaulted to modifying the default tags with react-md typography styles:

  • <h1> - <h6>
  • <p>
  • <caption>
  • <input>
  • <textarea>
  • <button>

Starting with v2, react-md will provide a much simpler CSS reset and not apply any typography to html tags. In addition, the default typography styles can be overridden or merged with new styles using the $rmd-typography-styles variable. The new @react-md/typography package also exports two new components for general text styling and reusable typography mixins.

Improved User Interaction States

A user interaction state is the feedback presented to the user when one of the following actions happens:

  • tap a clickable element on a touch device
  • click a clickable element with the mouse
  • focus an element with the keyboard
v1 implementation

v1 of react-md had a very "noisy" implementation of this with the ripple/ink effect which caused a 300ms animation each time the user interacted with a focusable element. This was okay for the tap and click behaviors but extremely obnoxious for keyboard users especially when tabbing through elements quickly.

In addition there would sometimes be :hover styles applied accidentally after a user touches an element on mobile since touch events also trigger the :hover state. The rewrite for v2 has solved these issues by introducing interaction states, providing mixins to dynamically apply styles based on an interaction state, and providing a new keyboard focus state.

Starting with v2

The @react-md/utils package provides an InteractionModeListener that is bundled into the Configuration component in the @react-md/layout package that helps determine which interaction mode the user is currently using. This can be used along with the following mixins for dynamically applying styles based on an interaction mode:

In addition, a new @react-md/states package has been added that implements the new behavior for the ink/ripple effect that will now only trigger after a click event instead of focus. The focus state has been drastically simplified to add blue box-shadow and sometimes add a slight background color change while keyboard focused. The new interaction states can be implemented for any non-react-md component as well with the following mixins:

Improved Accessibility and Keyboard Movement

The v1 release of react-md implemented some accessibility features and keyboard movement but the majority of the behavior was incorrect and caused more issues than it tried to solve. v2 is still not 100% perfect due to how differently screen readers behave, but should now follow the specs outlined on the WIA-ARIA Best Practices website. Due to these fixes a lot of components will now correctly require an aria-label or aria-labelledby prop which only required an id before.

The new keyboard movement behavior is drastically different now since components now correctly follow the recommendations outlined in the WIA-ARIA Best practices website. A new feature that is introduced in v2 is the ability to "type-to-focus" elements in some complex components. The user can now focus a component like the Tree or DropdownMenu and type letters to match the first child that starts with those letters. In addition, lot of work has been made into ensuring that keyboard focus is no longer lost while navigating between temporary elements like menus and dialogs and implementing the new aria-activedescendant movement when needed to keep focus on the correct components. It is highly recommended to check out the AutoComplete, DropdownMenu, and Tree components since the changes should be extremely noticeable and a few of the demos showcase this functionality.

Finally, the accessibility helpers have been created as reusable hooks in the @react-md/utils package so that non react-md components can implement this functionality as well.

  • useFocusOnMount - Focuses an element when the component mounts which defaults to the first focusable element child. Can be configured to focus the last child by default or a custom document.querySelector string.
  • usePreviousFocus - Used in temporary elements like menus and dialogs to help ensure that keyboard focus is not lost once the temporary element is closed.
  • useTabFocusWrap - A hook implementation of the FocusContainer that just provides a onKeyDown event handler for maintaining focus within a container.
  • useFocusMovement - Provides the functionality to focus elements with custom key configurations.
  • useActiveDescendantMovement - Provides the functionality to "focus" elements with custom key configurations by using the aria-activedescendant focus pattern which maintains focus on the "root" element
  • useKeyboardSearch - A low-level hook for providing the "type-to-focus" behavior on components as an onKeyDown event handler.
  • useKeyboardMovement - A low-level hook that is used by both the useFocusMovement and useActiveDescendantMovement to provide keyboard movement and optional search functionality.

Right to left Language Support

react-md will now automatically update the provided styles when a parent element (normally the root <html>) for dir="rtl" to support right to left languages. The following properties will be automatically inversed:

  • margin-left
  • margin-right
  • padding-left
  • padding-right
  • left
  • right

Since it might be required to update non-react-md components with these styles, the @react-md/utils package also exports the following mixins to implement this behavior:

The documentation site allows for a live preview for this functionality! If you are on desktop, click the right to left toggle button in the main header. If you are on mobile, click the kebab menu in the main header and then click the toggle right to left option.

This feature is enabled by default starting with v2 but can be disabled by updating the rmd-utils-enable-rtl variable:

$rmd-utils-enable-rtl: false;

@import "react-md/dist/react-md";

Convenience Configuration and Context Provider Components

v2 of react-md heavily uses the Context API to implement different features and configuration for things such as:

  • changing the default icons used throughout react-md
  • disabling the ripple behavior
  • configuring the theme for different components
  • determining the current application size

Check out the @react-md/layout package for more information since it provides a Configuration component that combines all of these context providers into a single component.

Around 50 new Components and 40 hooks

In order to allow more customization and reconfigurability within react-md, more low level components are now exported as well as moving some functionality within hooks.

All Material Icons Available as Components

Since it can be difficult to remember all the icon names or even mistyping a font icon name, react-md@v2 now provides every material icon as a FontIcon and SVGIcon implementation. At the time of writing these release notes, there are 932 icons available which means 1864 icon components. Check out the @react-md/material-icons package for more info and previewing every icon.

Scoped Packages

Since v2 of react-md ended up being a complete re-write, I wanted to create a way to be able to slowly update existing apps from v1 to v2 without forcing an immediate re-write. Related components will now be grouped and published together using the @react-md scope as well as the "root" react-md package that exports everything like normal. If you are completely new to react-md, I recommend just installing react-md while apps that are migrating should use the scoped packages.

Please see the scoped packages documentation for a more in-depth write up.

New Documentation Site

The documentation site has been re-written to use NextJS instead of the extremely outdated custom implementation with different webpack configs. This should hopefully allow contributors to get started more quickly and apply changes as needed. Each page should now also provide a table of contents to be able to quickly navigate to a specific heading, demo, documentation, etc. There are also some other small other documentation improvements that will be outlined below.

Note: The search behavior needs to be fixed since it was hacked together in about 20 minutes. You should be able to find the majority of components other than the grid components. These can be found in the @react-md/utils package as GridList and Grid.

Improved Documentation

After reviewing feedback about the v1 documentation site, the main problem areas were:

  • unable to view the full context of the example code
  • unable to live-edit the example code
  • unable to see the default compiled value for SCSS variables
  • not enough useful documentation around SCSS usage
  • not enough information about the example

To solve the first two issues, a custom sandbox generator script was created that resolves every dependency and file name for each demo and generates a sandbox json file. This allows for the new and improved DemoSandbox to render all the files for a demo as well as dynamically creating an editable code sandbox using the codesandbox define API. Each demo will now have a button to preview the code in the new DemoSandbox, create an editable sandbox with CodeSandbox, and a directly link to the source code within GitHub to help show the full context of the demos. In addition, each demo was updated to hopefully be a lot less complex than some that appeared in v1.

The third issue was solved by re-writing the sassdoc generator script to hackily force compile the scss variable. Now if a variable references another SCSS variable, the default compiled value can be viewed by enabling the "Default Compiled Value" switch for that variable. Check out the $rmd-alert-theme-values variable for a quick example.

The forth issue has not been solved completely, but there is now a new solution in place by being able to render examples a bit easier within SassDoc and showing the compiled value. The rmd-utils-rtl mixin has a pretty good example to reference for this functionality.

The last issue about missing information for the example has hopefully been solved by the new writing patterns throughout the documentation site. Each example should now have a bit more information about the components being used along with some background information about what the demo is trying to accomplish. Documentation is difficult so please provide feedback for what has been helpful and what has not.

GZip Size Comparison

The v2 release has decreased the UMD bundle size by 12.35% while the CSS bundles have increased by 18.56% - 18.75%:

v1 size
The gzipped UMD bundle size is:
 - dist/v1/umd/react-md.min.js 98.68 KB

The min and max gzipped CSS bundle sizes are:
 - dist/v1/css/react-md.yellow-red.min.css 13.2 KB
 - dist/v1/css/react-md.blue_grey-deep_purple.min.css 13.23 KB

v2 size
The gzipped UMD bundle size is:
 - dist/umd/react-md.production.min.js 86.49 KB
 - dist/umd/react-md-with-font-icons.production.min.js 196.05 KB
 - dist/umd/react-md-with-svg-icons.production.min.js 196.03 KB

The min and max gzipped CSS bundle sizes are:
 - dist/css/react-md.grey-deep_orange-200-light.min.css 15.65 KB
 - dist/css/react-md.indigo-blue-400-dark.min.css 15.71 KB

In addition, v2 should have finally solved the code splitting issue that existing in v1 and produce an even smaller bundle if every feature within react-md is not used.

In-depth Changelogs

This should be the main highlights for the v2 release. If you are interested in an in-depth package-by-package update and changelog, you can view one of the following changelogs: