React 18 + React Router v6 + Sidebar Navigation and a Sandpack Component

Rebeccca Peltz - Jan 29 '23 - - Dev Community

code

demo

I take advantage of the ease of not having to set up a router for React by using Next.js. Sometimes, Next.js is not the appropriate framework because mixing SSR with components that contain frames or otherwise interact with the window object causes a lot of extra steps. That’s when I turned in a different direction and installed React with the React Router.

I needed to set up a React application with routing and a component that accesses a frame. My project uses Sandpack, which creates a frame element connected to codesandbox.io. I dove into the current documentation on React 18 and how to integrate the v6 React Router with it. It was confusing, so I want to share what I ultimately came up with. I’m also using tailwind because I like this sidebar for Next.js, and it uses tailwind.

Create React App

You’ll start by running npx create-react-app test-app. Look at your

src/index.js.

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
 <React.StrictMode>
   <App />
 </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Enter fullscreen mode Exit fullscreen mode


.

Replace the code above with the code below

index.js.


import React from 'react';

// import ReactDOM from 'react-dom';
import './index.css';
import App from './App';

// );
import { createRoot } from 'react-dom/client';
const container = document.getElementById('root');
const root = createRoot(container); // createRoot(container!) if you use TypeScript
root.render(
   <App />
);

Enter fullscreen mode Exit fullscreen mode

Your App.js should look like this:

import logo from './logo.svg';
import './App.css';


function App() {
 return (
   <div className="App">
     <header className="App-header">
       <img src={logo} className="App-logo" alt="logo" />
       <p>
         Edit <code>src/App.js</code> and save to reload.
       </p>
       <a
         className="App-link"
         href="https://reactjs.org"
         target="_blank"
         rel="noopener noreferrer"
       >
         Learn React
       </a>
     </header>
   </div>
 );
}
export default App;
Enter fullscreen mode Exit fullscreen mode

We’ll also be replacing the code in App.js, but first, let’s decide on some routes and install the React Router: react-router-dom.

Install and Configure Router

For simplicity, we’ll create the three routes we are familiar with: Home, About, and Contact. These represent three pages in our single-page app.

These pages can be created by just adding src/Home.js, src/About.js, and src/Contact.js. They all have the same code other than self-identifying text. Repeat what you see for About.js and Contact.js and change the H1 element text to identify as “About” or “Contact”

.js

function Home() {
   return (
     <div>
       <h1>This is the home page</h1>
     </div>
   );
 }
  export default Home;
Enter fullscreen mode Exit fullscreen mode

Now we have three routes. The Home page will be accessed as “/”, the About page will be accessed as “/about”, and the Contact page will be accessed as “/contact”. With this mapping understood, we can create our Route configuration and Sidebar navigation.

33 Install react-router-dom

Start by adding the router to your project:

npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode

Now replace the code in your App.js with this:

import { Routes, Route, BrowserRouter } from "react-router-dom"
import Home from "./Home"
import About from "./About"
import Contact from "./Contact"
import Layout from "./components/Layout";

function App() {
 return (
   <div className="App">
     <BrowserRouter>
       <Routes>
         <Route element={<Layout />} >
           <Route path="/" element={<Home />} />
           <Route path="about" element={<About />} />
           <Route path="contact" element={<Contact />} />
         </Route>
       </Routes>
     </BrowserRouter>
   </div>
 )
}

export default App
Enter fullscreen mode Exit fullscreen mode


.

Notice that we wrap the individual Routes in a singular Route component. Then we wrap the Route component in a BrowserRouter component. For the Home page route, we use “/” to indicate the path. For the non-root paths, we just use the path's name without a “/”.
This seems simple now, but it took me a lot of trial and error until I came across this blog which helped a lot.

To build on what the blogger helped me with, let me show you how to add sidebar navigation and Sandpack, which is helpful if you want to manage code sandboxes on your website.
Create Sidebar Navigation
As mentioned above, this Sidebar component is adapted from a blogger’s Next.js sidebar. It uses React Router, which means you will need to use the <Outlet /> component to provide a place for your page to render as you navigate through the sidebar. Read more about the Outlet component here.

The Layout component that container sidebar navigation relies on tailwind. Here are the steps to getting tailwind installed.

Tailwind Install

npm install -D tailwindcss

Create the config files which include tailwind.config.js and postcss.config.js.

npx tailwindcss init -p

These file should look like this:

tailwind.config.js

/** @type {import('tailwindcss').Config} */
module.exports = {
 content: [
   "./src/**/*.{js,jsx,ts,tsx}",
   "./public/index.html",
 ],
 theme: {
   extend: {},
 },
 plugins: [],
}
Enter fullscreen mode Exit fullscreen mode
postcss.config.js
module.exports = {
 plugins: {
   tailwindcss: {},
   autoprefixer: {},
 },
}
Enter fullscreen mode Exit fullscreen mode

Then add

@tailwind base;
@tailwind components;
@tailwind utilities;
Enter fullscreen mode Exit fullscreen mode

to your src/index.css file.

If you’re using Visual Studio Code and it flags the entries above, you can get the PostCSS Language Support extension to clear that up.

Now we’re ready to write the code for the Layout component.

Layout with Sidebar Navigation and Outlet “Placeholder”

Here’s the code I used to create the Layout component that includes NavLinks and Outlet from the React router.

It’s helpful to create an array of data about your routes and use the JavaScript map command to create the navigation code.

import { NavLink, Outlet } from "react-router-dom";
export default function Layout() {
   const testMenuItems = [
       {
           href: '/',
           title: 'Introduction',
       },
       {
           href: 'about',
           title: 'About',
       },
       {
           href: 'contact',
           title: 'Contact',
       }
   ];

   <li><NavLink to="/">Home</NavLink></li>
   return (
       <div className='min-h-screen flex flex-col'>
           <header className='bg-gray-200 text-black sticky top-0 h-14 flex justify-center items-center font-semibold uppercase'>
               Cloudinary Actions
           </header>
           <div className='flex flex-col md:flex-row flex-1'>
               <aside className='bg-gray-100 w-full md:w-60'>
                   <nav>
                       <ul>
                           {testMenuItems.map(({ href, title }) => (
                               <li className='m-2' key={title}>
                                   <NavLink to={href} >
                                       <p className={'text-black'}>{title}</p>
                                   </NavLink>
                               </li>
                           ))}
                       </ul>
                   </nav>
               </aside>
               <main className={'flex-1'}>
                   <Outlet />
               </main>
           </div>
       </div>
   );
}
Enter fullscreen mode Exit fullscreen mode

Add a Sandpack Component

Now, the whole reason I got into adding a router is to use React with routing on pages that contain the Sandbag component provided by codesandbox.io. I want to host my own code sandboxes. I’m so pleased that codesandbox.io is making this possible.

Here’s a sample Sandpack component. It produces two code sandboxes on a web page. These sandboxes can be modified, and the code can be run by the user. I’m a curriculum developer at Cloudinary, so they are used to create image elements. Notice that you don’t need to install the Cloudinary library to have Sandbag run Cloudinary code because the codesandbox.io frame will take care of that. There’s a lot of code in the component below because I’ve set up two sandboxes for this page, and I’m trying out Sandbag options.

npm install @cloudinary/url-gen

CldSample Component

import '../App.css';
import { Sandpack } from "@codesandbox/sandpack-react";

const cloudinaryAdvancedImage = `
import {AdvancedImage} from '@cloudinary/react';
import {Cloudinary} from "@cloudinary/url-gen";
import {Transformation} from "@cloudinary/url-gen";
export default function App() {
 const cld = new Cloudinary({
   cloud: {
     cloudName: 'demo'
   }
 });
 const myImage = cld.image('front_face');
 // return <h1>Hello Sandpack</h1>
 return (
   <div>
     <AdvancedImage cldImg={myImage} />
   </div>
 )
}`

const cloudinaryAdvancedImageWithTransformations = `
import {AdvancedImage} from '@cloudinary/react';
import {Cloudinary} from "@cloudinary/url-gen";
import {Transformation} from "@cloudinary/url-gen";

// Import required actions.
import {thumbnail, scale} from "@cloudinary/url-gen/actions/resize";
import {byRadius} from "@cloudinary/url-gen/actions/roundCorners";
import {sepia} from "@cloudinary/url-gen/actions/effect";
import {source} from "@cloudinary/url-gen/actions/overlay";
import {opacity,brightness} from "@cloudinary/url-gen/actions/adjust";
import {byAngle} from "@cloudinary/url-gen/actions/rotate"

// Import required qualifiers.
import {image} from "@cloudinary/url-gen/qualifiers/source";
import {Position} from "@cloudinary/url-gen/qualifiers/position";
import {compass} from "@cloudinary/url-gen/qualifiers/gravity";
import {focusOn} from "@cloudinary/url-gen/qualifiers/gravity";
import {FocusOn} from "@cloudinary/url-gen/qualifiers/focusOn";
export default function App() {
 const cld = new Cloudinary({
   cloud: {
     cloudName: 'demo'
   }
 });
 const myImage = cld.image('front_face');
 // Apply the transformation.
 myImage
 .resize(thumbnail().width(150).height(150).gravity(focusOn(FocusOn.face())))  // Crop the image.
 .roundCorners(byRadius(20))    // Round the corners.
 .effect(sepia())  // Apply a sepia effect.
 .overlay(   // Overlay the Cloudinary logo.
   source(
     image('cloudinary_icon_blue')
       .transformation(new Transformation()
       .resize(scale(50)) // Resize the logo.
         .adjust(opacity(60))  // Adjust the opacity of the logo.
       .adjust(brightness(200)))  // Adjust the brightness of the logo.      
   )
   .position(new Position().gravity(compass('south_east')).offsetX(5).offsetY(5))   // Position the logo.
 )
 .rotate(byAngle(10))  // Rotate the result.
 .format('png');   // Deliver as PNG.
 return (
   <div>
     <AdvancedImage cldImg={myImage} />
   </div>
 )
};`
export default function CldSample() {
   return (
       <div className="code-container">
           <h1>Cloudinary Transformations</h1>
           <h2>Render an Image with Advanced Image</h2>
           <Sandpack
               // You can change these examples!
               // Try uncommenting any of these lines
               theme="dark"
               // theme="light"
               // theme="auto"
               template="react"
               files={{
                   "/App.js": cloudinaryAdvancedImage,
               }}
               customSetup={{
                   dependencies: {
                       "@cloudinary/react": "^1.9.0",
                       "@cloudinary/url-gen": "^1.8.7",
                   },
               }}
               options={{
                   showNavigator: true,
                   showTabs: true,
                   showLineNumbers: false, // default - true
                   showInlineErrors: true, // default - false
                   wrapContent: true, // default - false
                   editorHeight: 350, // default - 300
                   editorWidthPercentage: 60, // default - 50
                   autorun: false,
                   recompileMode: "delayed", //default is immediate
                   recompileDelay: 300,
                   resizablePanels: true, //default
               }}
           />
           <h2>Create Transformations</h2>
           <Sandpack
               // You can change these examples!
               // Try uncommenting any of these lines
               theme="dark"
               // theme="light"
               // theme="auto"
               template="react"
               files={{
                   "/App.js": cloudinaryAdvancedImageWithTransformations,
               }}
               customSetup={{
                   dependencies: {
                       "@cloudinary/react": "^1.9.0",
                       "@cloudinary/url-gen": "^1.8.7",
                   },
               }}
               options={{
                   showNavigator: true,
                   showTabs: true,
                   showLineNumbers: false, // default - true
                   showInlineErrors: true, // default - false
                   wrapContent: true, // default - false
                   editorHeight: 350, // default - 300
                   editorWidthPercentage: 60, // default - 50
                   autorun: false,
                   recompileMode: "delayed", //default is immediate
                   recompileDelay: 300,
                   resizablePanels: true, //default
               }}
           />
       </div>
   )
}
Enter fullscreen mode Exit fullscreen mode

This is what your page will look like if you get this running.

Project File System Structure

I hope this helps anyone working with React 18 and the React Router

Project Structure
Demo Web Page

. . . . . . . . . . .