Dynamically Loading Material-UI Icons Using React Router Query Params

The purpose of this tutorial is to demonstrate how to use query params in React Router, including using nested routes. However, instead of a boring how-to, I created a tutorial that includes:

  • Dynamically loading Material-UI (MUI) icons.
  • Using Material-UI Buttons as React Router Links.
  • Programmatically setting the route using the useHistory React Router Hook.
  • Extracting query params using the useLocation React Router Hook.

In this demo, we will build an app that takes a user-input MUI icon name, routes to appropriate components with the icon name as a query param (because we’re demoing that), extracts the icon name from the URL, and dynamically imports just that icon from @material-ui/icons.

I believe knowledge sticks better when something useful is built with it. Let’s get to coding!

Note: There are minor differences between using query params and URL params in React Router. CodeSandboxes for both query param and URL param methods are in the Resources section.

React-Router App Layout and Code Architecture

MUI Icon previewer

The app is simple: a side drawer that contains a link to the /home route and a <main> element where the routing controls what is displayed. Within the <main> element, there are three components that get swapped out: HomeIconRenderer, and IconDetails.

From the URL, IconRenderer (/previewicon) looks to be a parent of IconDetails (/previewicon/details). However, they are sibling elements in terms of app architecture.

App.js Router Code

I’ll focus on the React Router code:

//In App.js
export default function App() {
  const classes = useStyles();
return (
  <BrowserRouter>
//skipping a few lines of <Drawer> code...
    <main className={classes.content}>
      <Switch>
        <Route exact path={"/"} component={Home} />
        <Route exact path={"/home"} component={Home} />
        <Route
          exact
          path={"/previewicon"}
          component={IconRenderer}
        />
        <Route
          exact
          path={"/previewicon/details"}
          component={IconDetails}
        />
      </Switch>
    </main>
  </BrowserRouter>
 );
}

The app is wrapped in <BrowserRouter>.

Within that, <Switch> is used to swap out what component is rendered in the JSX. It is essentially a JavaScript switch statement for React Router where the case is based on the URL.

Interestingly, I had to set two Route components for <Home> since I wanted both / and /home to render the <Home> component. Another technique would be <Route exact path={“/” | “/home”} component={Home} />, but it wasn’t playing well with the child routes. Also, the / route must have the exact flag set. This means that the route will only match if the URL is exactly /. Without exact, all URLs would route to the <Home> component.

Home.js Router code

The <Home> component contains an MUI TextField and Button. There are two items of particular interest:

  • The useHistory Hook — I created const history = useHistory(); and then used it to programmatically update the URL when a user presses enter after typing in the TextField:
onKeyPress={(e) => {
if (e.key === "Enter") {
history.push(`/previewicon?iconName=${enteredValue}`);
}
}}
  • The MUI Button containing a React Router Link. The component param contains a React Router Link — not a Material-UI Link component.
<Button
  component={Link}
  style={{ marginTop: 20 }}
  to={`/previewicon?iconName=${enteredValue}`}
  variant="contained"
  color="primary"
>
Search Icons
</Button>

IconRenderer.js Code

<IconRenderer> extracts the parameter from the URL. It then uses useStateuseRef, and an async function to dynamically load the icon according to what the user input in the <Home> component. After this, it renders the icon.

Rendered icon

The code for extracting a value from query params is slightly different (and more verbose) than for extracting URL params:

function useQuery() {
  return new URLSearchParams(useLocation().search);
}

let query = useQuery();
const iconName = query.get("iconName");

This code uses the useLocation Hook from React Router.

URL param extraction (if you want to use that instead):

const iconName = props.match.params.iconname;

The async function is in a separate util file since it is shared by two:


const importIcon = async (ref, iconName, setLoading) => {
  try {
    const namedImport = await import(`@material-ui/icons/${iconName}`);
    ref.current = namedImport;
  } catch (err) {
    console.log(err);
  } finally {
    return setLoading(false);
  }
};

Simply pass in the reficonName (originally extracted from the URL), and a loading callback.

IconDetails.js code

Admittedly, <IconDetails> is a bit of a contrived example. I simply wanted to demo how to pass query params to child routes.

<IconDetails> renders the icon in several different color schemes:

Icon in different color schemes

I found it easy to use query params in child components. The URL params method was a little more difficult. Ultimately, it comes down to making sure your URLs have appropriate /:, and other syntax. Here’s an example of what the child URL param syntax looks like (from Home.js):

<Route
exact
path={"/iconpreviewer/details/:iconname"}
component={IconDetails}
/>

In IconDetail.js, the icon with colors was mapped like so:

<div>
  <h4>Icon: {iconName}</h4>
  {["primary", "secondary", "action", "disabled"].map(
    (color, index) => (
      <UserSpecifiedIcon color={color} key={index} />
    )
  )}
</div>

Both query params and URL params can get the job done. My biggest point of frustration was simply my own errors in getting the Route (path) and Link (to) syntax correct.

Walking through a JavaScript tutorial like this will give you confidence that you can make React Router work for your apps too.

Resources

If you’d like another React Router demo, take a look at adding Links inside MUI alerts.

These two resources were extremely helpful in creating the code for this tutorial:

https://blog.pshrmn.com/simple-react-router-v4-tutorial/

https://medium.com/@erickhoury/react-dynamically-importing-svgs-and-render-as-react-component-b764b6475896

Share this post:

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.