├── .gitignore ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── manifest.json └── index.html └── src ├── pages ├── subpages │ ├── Subpage1.js │ └── Subpage2.js ├── Page2.js └── Page1.js ├── index.js ├── NotFound.js ├── Login.js ├── utils └── RouteWithSubRoutes.js ├── App.js ├── Routes.js ├── Home.js └── styles.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gangludev/react-nested-routes/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gangludev/react-nested-routes/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gangludev/react-nested-routes/HEAD/public/logo512.png -------------------------------------------------------------------------------- /src/pages/subpages/Subpage1.js: -------------------------------------------------------------------------------- 1 | const Subpage1 = () => { 2 | return <>Hello from subpage1; 3 | }; 4 | 5 | export default Subpage1; 6 | -------------------------------------------------------------------------------- /src/pages/subpages/Subpage2.js: -------------------------------------------------------------------------------- 1 | const Subpage2 = () => { 2 | return <>Hello from subpage2; 3 | }; 4 | 5 | export default Subpage2; 6 | -------------------------------------------------------------------------------- /src/pages/Page2.js: -------------------------------------------------------------------------------- 1 | const Page2 = () => { 2 | return
Hello from Page 2!
; 3 | }; 4 | 5 | export default Page2; 6 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | import './styles.css'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /src/NotFound.js: -------------------------------------------------------------------------------- 1 | const NotFound = () => { 2 | return ( 3 | <> 4 |
5 |

6 | 😰 7 |
8 | 404 Not Found 9 |

10 |
11 | 12 | ); 13 | }; 14 | 15 | export default NotFound; 16 | -------------------------------------------------------------------------------- /src/Login.js: -------------------------------------------------------------------------------- 1 | import { Link } from 'react-router-dom'; 2 | 3 | const Login = (props) => { 4 | return ( 5 | <> 6 |
7 |

Login to the app

8 | 9 | Login 10 | 11 |
12 | 13 | ); 14 | }; 15 | 16 | export default Login; 17 | -------------------------------------------------------------------------------- /src/utils/RouteWithSubRoutes.js: -------------------------------------------------------------------------------- 1 | import { Route } from 'react-router-dom'; 2 | 3 | const RouteWithSubRoutes = (route) => { 4 | return ( 5 | ( 8 | 9 | )} 10 | /> 11 | ); 12 | }; 13 | 14 | export default RouteWithSubRoutes; 15 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { BrowserRouter, Redirect, Switch, Route } from 'react-router-dom'; 2 | import NotFound from './NotFound'; 3 | import routes from './Routes'; 4 | import RouteWithSubRoutes from './utils/RouteWithSubRoutes'; 5 | 6 | function App() { 7 | return ( 8 | 9 | 10 | 11 | {routes.map((route, i) => ( 12 | 13 | ))} 14 | 15 | 16 | 17 | ); 18 | } 19 | 20 | export default App; 21 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/Routes.js: -------------------------------------------------------------------------------- 1 | import Home from './Home'; 2 | import Login from './Login'; 3 | import Page1 from './pages/Page1'; 4 | import Page2 from './pages/Page2'; 5 | import Subpage1 from './pages/subpages/Subpage1'; 6 | import Subpage2 from './pages/subpages/Subpage2'; 7 | 8 | const routes = [ 9 | { 10 | path: '/login', 11 | component: Login, 12 | }, 13 | { 14 | path: '/home', 15 | component: Home, 16 | routes: [ 17 | { 18 | path: '/home/page1', 19 | component: Page1, 20 | routes: [ 21 | { 22 | path: '/home/page1/subpage1', 23 | component: Subpage1, 24 | }, 25 | { 26 | path: '/home/page1/subpage2', 27 | component: Subpage2, 28 | }, 29 | ], 30 | }, 31 | { 32 | path: '/home/page2', 33 | component: Page2, 34 | }, 35 | ], 36 | }, 37 | ]; 38 | 39 | export default routes; 40 | -------------------------------------------------------------------------------- /src/pages/Page1.js: -------------------------------------------------------------------------------- 1 | import { Link, Switch } from 'react-router-dom'; 2 | import RouteWithSubRoutes from '../utils/RouteWithSubRoutes'; 3 | 4 | const Page1 = ({ routes }) => { 5 | const menu = [ 6 | { 7 | path: '/home/page1/subpage1', 8 | name: 'Subpage 1', 9 | }, 10 | { 11 | path: '/home/page1/subpage2', 12 | name: 'Subpage 2', 13 | }, 14 | ]; 15 | 16 | return ( 17 |
18 |
19 |
    20 | {menu.map((menuItem) => ( 21 |
  • 22 | {menuItem.name} 23 |
  • 24 | ))} 25 |
26 |
27 | 28 | 29 | {routes.map((route, i) => ( 30 | 31 | ))} 32 | 33 |
34 | ); 35 | }; 36 | 37 | export default Page1; 38 | -------------------------------------------------------------------------------- /src/Home.js: -------------------------------------------------------------------------------- 1 | import { Switch, Link } from 'react-router-dom'; 2 | import RouteWithSubRoutes from './utils/RouteWithSubRoutes'; 3 | 4 | const Home = ({ routes }) => { 5 | const menu = [ 6 | { 7 | path: '/home/page1', // the url 8 | name: 'Page1', // name that appear in Sidebar 9 | }, 10 | { 11 | path: '/home/page2', 12 | name: 'Page2', 13 | }, 14 | ]; 15 | 16 | return ( 17 | <> 18 |
19 | {/* This can be treated as a sidebar component */} 20 |
21 |

React Nested Routes

22 | 23 |
    24 | {menu.map((menuItem) => ( 25 |
  • 26 | {menuItem.name} 27 |
  • 28 | ))} 29 |
30 |
31 | 32 | 33 | {routes.map((route, i) => ( 34 | 35 | ))} 36 | 37 |
38 | 39 | ); 40 | }; 41 | 42 | export default Home; 43 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | 5 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 6 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 7 | } 8 | 9 | .login { 10 | min-height: 100vh; 11 | display: flex; 12 | flex-direction: column; 13 | align-items: center; 14 | justify-content: center; 15 | font-size: calc(10px + 2vmin); 16 | } 17 | 18 | .text-center { 19 | text-align: center; 20 | } 21 | 22 | .button { 23 | background-color: #61dafb; 24 | border: 1px solid #61dafb; 25 | color: white; 26 | padding: 12px 24px; 27 | font-size: 16px; 28 | text-decoration: none; 29 | } 30 | 31 | .home { 32 | display: flex; 33 | height: 100vh; 34 | } 35 | 36 | .sidebar { 37 | min-width: 300px; 38 | background: #f2f5fa; 39 | height: 100%; 40 | box-sizing: border-box; 41 | padding: 18px; 42 | font-size: 14px; 43 | } 44 | 45 | .sidebar h2 { 46 | padding-left: 12px; 47 | } 48 | 49 | .sidebar ul { 50 | list-style: none; 51 | padding-left: 0; 52 | } 53 | 54 | .sidebar li { 55 | padding: 12px; 56 | border-bottom: 1px solid #e2e2e2; 57 | transition: all 0.2s ease; 58 | } 59 | 60 | .sidebar li:hover { 61 | background: #e2e9f5; 62 | } 63 | 64 | .sidebar li a { 65 | color: black; 66 | text-decoration: none; 67 | } 68 | 69 | .main { 70 | display: flex; 71 | align-items: center; 72 | justify-content: center; 73 | height: 100%; 74 | width: 100%; 75 | } 76 | 77 | .navbar { 78 | border-bottom: 2px solid #f2f5fa; 79 | width: 100%; 80 | max-height: 60px; 81 | box-sizing: border-box; 82 | display: flex; 83 | align-items: center; 84 | justify-content: flex-end; 85 | padding: 0 18px; 86 | } 87 | 88 | .navbar ul { 89 | list-style-type: none; 90 | display: flex; 91 | justify-content: end; 92 | } 93 | 94 | .navbar li a { 95 | text-decoration: none; 96 | color: black; 97 | padding: 18px 12px; 98 | margin: 0 4px; 99 | } 100 | 101 | .navbar li a:hover { 102 | background: #e2e9f5; 103 | } 104 | 105 | .page-main { 106 | display: flex; 107 | flex-direction: column; 108 | width: 100%; 109 | } 110 | --------------------------------------------------------------------------------