Search code examples
javascriptreactjsreact-router-dom

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. In react project


I am building a react app to learn MUI opportunities and get this error in Browser:

Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Check the render method of App. Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.

Here is my App component:

import React from 'react';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import Navbar from './components/Navbar';
import routes from './routes';

const App = () => {
  return (
    <Router>
      <Navbar />
      <div className="container mt-4">
        <Routes>
          {routes.map((route, index) => (
            <Route key={index} path={route.path} element={route.component} />
          ))}
        </Routes>
      </div>
    </Router>
  );
};

export default App;

routes:

import Blog from "./pages/Blog";
import Home from "./pages/Home";
import Team from "./pages/Team";

const routes = [
  { path: '/', component: Home, exact: true }, // Use the component class/function, not JSX element
  { path: '/blog', component: Blog },
  { path: '/team', component: Team },
];

export default routes;

Navbar:

import React, { useState } from "react";
import AppBar from "@mui/material/AppBar";
import Toolbar from "@mui/material/Toolbar";
import Button from "@mui/material/Button";
import { Link, useLocation } from "react-router-dom";
import HistoryDrawer from "./HistoryDrawer";

const Navbar = () => {
  const [openDrawer, setOpenDrawer] = useState(false);
  const location = useLocation();

  const toggleDrawer = () => {
    setOpenDrawer(!openDrawer);
  };
  return (
    <React.Fragment>
      <AppBar position="static">
        <Toolbar>
          <Button
            color="inherit"
            component={Link}
            to="/"
            selected={location.pathname === "/"}
          >
            Home
          </Button>
          <Button
            color="inherit"
            component={Link}
            to="/blog"
            selected={location.pathname === "/blog"}
          >
            Blog
          </Button>
          <Button
            color="inherit"
            component={Link}
            to="/team"
            selected={location.pathname === "/team"}
          >
            Team
          </Button>
          <Button
            edge="start"
            color="inherit"
            aria-label="menu"
            onClick={toggleDrawer}
          >
            History
          </Button>
        </Toolbar>
      </AppBar>
      <HistoryDrawer toggleDrawer={toggleDrawer} openDrawer={openDrawer}/>
    </React.Fragment>
  );
};

export default Navbar;

Solution

  • The only issue/discrepancy I see is that the Route component's element prop expects a ReactNode type, not a ReactElement. In other words, element takes JSX. I will assume the Blog, Home, and Team default export/imports are correct, but if there is an issue with any of them then check that they are all also default exported from their respective files.

    You can either pass routes with JSX already:

    import Blog from "./pages/Blog";
    import Home from "./pages/Home";
    import Team from "./pages/Team";
    
    const routes = [
      { path: '/', component: <Home />, },
      { path: '/blog', component: <Blog /> },
      { path: '/team', component: <Team /> },
    ];
    

    Or render as JSX when mapping:

    const App = () => {
      return (
        <Router>
          <Navbar />
          <div className="container mt-4">
            <Routes>
              {routes.map((route) => {
                const Component = route.component;
                return (
                  <Route
                    key={route.path}
                    path={route.path}
                    element={<Component />}
                  />
                );
              })}
            </Routes>
          </div>
        </Router>
      );
    };
    

    You could simplify the routes code a bit as well by adhering the the RouteObject type:

    import Blog from "./pages/Blog";
    import Home from "./pages/Home";
    import Team from "./pages/Team";
    
    const routes = [
      { path: '/', elememt: <Home />, },
      { path: '/blog', element: <Blog /> },
      { path: '/team', element: <Team /> },
    ];
    
    const App = () => {
      return (
        <Router>
          <Navbar />
          <div className="container mt-4">
            <Routes>
              {routes.map((route, index) => (
                <Route key={index} {...route} />
              ))}
            </Routes>
          </div>
        </Router>
      );
    };
    

    Or use the useRoutes hook:

    import React from 'react';
    import { BrowserRouter as Router, useRoutes } from 'react-router-dom';
    import Navbar from './components/Navbar';
    import routes from './routes';
    
    const App = () => {
      const appRoutes = useRoutes(routes);
      return (
        <Router>
          <Navbar />
          <div className="container mt-4">
            {appRoutes}
          </div>
        </Router>
      );
    };