One place for hosting & domains

      Router

      Configure Linux as a Router (IP Forwarding)


      A computer network is a collection of computer systems that can communicate with each other. To communicate with a computer that’s on a different network, a system needs a way to connect to that other network. A router is a system that acts as a intermediary between multiple different networks. It receives traffic from one network that is ultimately destined for another. It’s able to identify where a particular packet should be delivered and then forward that packet over the appropriate network interface.

      There are lots of options for off-the-shelf router solutions for both home and enterprise. In most cases, these solutions are preferred as they are relatively easy to configure, have lots of features, tend to have a user-friendly management interface, and may come with support options. Under the hood, these routers are stripped down computers running common operating systems, like Linux.

      Instead of using one of these pre-built solutions, you can create your own using any Linux server, like a Linode Compute Instance. Using routing software like iptables, you have total control over configuring a router and firewall to suit your individual needs. This guide covers how to configure a Linux system as a basic router, including enabling IP forwarding and configuring iptables.

      Use Cases for a Cloud-based Router

      Many workloads benefit from custom routing or port forwarding solutions, including those workloads hosted on cloud platforms like Linode. For example, it’s common practice for security-minded applications to connect most of their systems together through a private network, like a VLAN. These systems might need access to an outside network, like other VLANs or the public internet. Instead of giving each one their own interface to the other network, one system on the private network can act as a router. The router is configured with multiple network interfaces (one to the private VLAN and one to other network) and forwards packets from one interface to another. This can make monitoring, controlling, and securing traffic much easier, as it can all be done from a single system. Linode Compute Instances can be configured with up to 3 interfaces, each connecting to either the public internet or a private VLAN.

      • Connect systems on private VLAN to the public internet.
      • Connect systems on two separate private VLANs.
      • Forward IPv6 addresses from a /56 routed range.

      Configure a Linux System as a Router

      1. Deploy at least 2 Compute Instances (or other virtual machines) to the same data center. All systems should be connected to the same private network, like a
        VLAN. One system should be designated as the router and should also be connected to the public internet or a different private network. See
        Deploy Compute Instances.
      2. Enable IP forwarding on the Compute Instance designated as the router. See
        Enable IP Forwarding.
      3. Configure the routing software on that same instance (the router). This guide covers using iptables, but you can also use other software. See
        Configure iptables.
      4. Define a gateway on each system other than the router. This gateway should point to the router’s IP address on that network. See
        Define the Gateway.

      Deploy Compute Instances

      To get started, you can use the Linode platform to deploy multiple Compute Instances. These can mimic a basic application that is operating on a private VLAN with a single router. If you already have an application deployed and just wish to know how to configure ip forwarding or iptables, you can skip this section.

      1. Deploy 2 or more Compute Instances and designate one as the router. Each of these should be deployed to the same region. On the deployment page, you can skip the VLAN section for now. See
        Creating a Compute Instance to learn how to deploy Linode Compute Instances.

      2. On each Compute Instance other than the router, edit the instance’s configuration profile. See
        Managing Configuration Profiles for information on viewing and editing configuration profiles.

        • On the Compute Instance designated as the router, leave eth0 as the public internet and set eth1 to be configured as a VLAN. Enter a name for the VLAN and assign it an IP address from whichever subnet range you wish to use. For instance, if you wish to use the 10.0.2.0/24 subnet range, assign the IP address 10.0.2.1/24. By convention, the router should be assigned the value of 1 in the last segment.
        • On each Compute Instance other than the router, remove all existing network interfaces. Set eth0 as a VLAN, select the VLAN you just created, and enter another IP address within your desired subnet (such as 10.0.2.2/24 and 10.0.2.3/24).
      3. Confirm that
        Network Helper is enabled and reboot each Compute Instance for the changes to take effect.

      4. Test the connectivity on each Compute Instance to ensure proper configuration. Log in to each instance and confirm the following is true:

        • Ping the VLAN IPv4 address of another system within the same VLAN. Each Compute Instance should be able to ping the IP addresses of all other instances within that VLAN.

          ping 10.0.2.1
          
        • Ping an IP address or website of a system on the public internet. This ping should only be successful for the Compute Instance configured as the router.

          ping linode.com
          

      Enable IP Forwarding

      IP forwarding plays a fundamental role on a router. This is the functionality that allows a router to forward traffic from one network interface to another network interface. In this way, it allows computers on one network to reach a computer on a different network (when configured along with routing software). Forwarding for both IPv4 and IPv6 addresses are controlled within the Linux kernel. The following kernel parameters are used to enable or disable IPv4 and IPv6 forwarding, respectively.

      • IPv4: net.ipv4.ip_forward or net.ipv4.conf.all.forwarding
      • IPv6: net.ipv6.conf.all.forwarding

      By default, forwarding is disabled on most Linux systems. To configure Linux as a router, this needs to be enabled. To enable forwarding, the corresponding parameter should be set to 1. A value of 0 indicates that forwarding is disabled. To update these kernel parameters, edit the /etc/sysctl.conf file as shown in the steps below.

      1. Log in to the Linux system you intend to use as a router. You can use
        SSH or
        Lish (if you’re using a Linode Compute Instance).

      2. Determine if IPv4 forwarding is currently enabled or disabled. The command below outputs the value of the given parameter. A value of 1 indicates that the setting is enabled, while 0 indicates it is disabled. If you intend to configure IPv6 forwarding, check that kernel parameter as well.

        sudo sysctl net.ipv4.ip_forward
        

        If this parameter is disabled (or otherwise not in the desired state), continue with the instructions below.

      3. Open the file /etc/sysctl.conf using your preferred command-line editor, such as
        nano.

        sudo nano /etc/sysctl.conf
        
      4. Find the line corresponding with the type of forwarding you wish to enable, uncomment it, and set the value to 1. Alternatively, you can add the lines anywhere in the file.

        File: /etc/sysctl.conf
        1
        2
        3
        4
        5
        6
        7
        
        ...
        ## Configure IPv4 forwarding
        net.ipv4.ip_forward = 1
        
        ## Configure IPv6 forwarding
        net.ipv6.conf.all.forwarding = 1
        ...
      5. After the changes have been saved, apply the changes by running the following command or by rebooting the machine.

        sudo sysctl -p
        

      Configure iptables

      The iptables utility can serve as both a firewall (through the default filter table) and as a router (such as when using the nat table). This section covers how to configure iptables to function as a basic router. If you prefer, you can use any other firewall or routing software, such as
      nftables or a commercial application.

      1. Log in to the Linux system you intend to use as a router. You can use
        SSH or
        Lish (if you’re using a Linode Compute Instance).

      2. Review the existing iptables rules. If you are on a fresh installation of Linux and do not have any preconfigured rules, the output of the below command should by empty.

        iptables-save
        

        If do receive output, look for any rules that might interfere with your intended configuration. If you are unsure, you may want to consult your system administrator or the
        iptables documentation. If needed, you can flush your iptables rules and allow all traffic.

        iptables -F
        iptables -X
        iptables -t nat -F
        iptables -t nat -X
        iptables -t mangle -F
        iptables -t mangle -X
        iptables -P INPUT ACCEPT
        iptables -P OUTPUT ACCEPT
        iptables -P FORWARD ACCEPT
        
      3. Configure iptables to allow port forwarding. This is the default setting for many systems.

        iptables -A FORWARD -j ACCEPT
        
      4. Next, configure NAT (
        network address translation) on iptables. This modifies the IP address details in network packets, allowing all systems on the private network to share the same public IP address of the router. Add the following iptables rule, replacing 10.0.2.0/24 with the subnet of your private VLAN.

        iptables -t nat -s 10.0.2.0/24 -A POSTROUTING -j MASQUERADE
        

        You can also forgo specifying any specific subnet and allow NAT over all traffic by using the command below.

        iptables -t nat -A POSTROUTING -j MASQUERADE
        
      5. By default, iptables rules are ephemeral. To make these changes persistent, install the iptables-persistent package. When you do this, the rules saved within /etc/iptables/rules.v4 (and rules.v6 for IPv6) are loaded when the system boots up. You can continue making changes to iptables as normal. When you are ready to save, save the output of
        iptables-save to the /etc/iptables/rules.v4 (or rules.v6) file. For more information, see the relevant section with the
        Controlling Network Traffic with iptables guide.

        iptables-save | sudo tee /etc/iptables/rules.v4
        

      Define the Gateway

      The last step is to manually adjust the network configuration settings for each Compute Instance other than the router.

      1. Log in to the
        Cloud Manager and disable
        Network Helper for each non-router Compute Instance you’ve deployed. While Network Helper was useful for automatically configuring the VLAN IP addresses, the configuration files controlled by Network Helper now need to be manually edited.

      2. Log in to each Linux system that is not designated as the router. You can use
        SSH or
        Lish (if you’re using a Linode Compute Instance).

      3. Edit the configuration file that contains the settings for the private VLAN interface. This name and location of this file depends on the Linux distribution you are using. See the
        Manual Network Configuration on a Compute Instance series of guides and select the specific guide for your distribution. For a system running
        ifupdown on Debian 10, the network configuration is typically stored within /etc/network/interfaces.

        sudo nano /etc/network/interfaces
        
      4. Within this file, adjust the parameter that defines the gateway for the VLAN interface. The value should be set to the IP address assigned to the router’s VLAN interface, such as 10.0.2.1 if you’ve used the example in this guide. For a system running
        ifupdown on Debian 10, you can add the gateway parameter in the location shown in the example below.

        File: /etc/network/interfaces
        1
        2
        3
        4
        
        ...
        iface eth0 inet static
            address 10.0.2.2/24
            gateway 10.0.2.1
      5. After those settings have been saved, restart the Compute Instance or run the corresponding command to apply the changes. Continuing to use
        ifupdown as an example, run the command below to apply the new network configuration settings.

        sudo ifdown eth0 && sudo ip addr flush eth0 && sudo ifup eth0
        

      Test the Connection

      To verify the configuration settings are correct, run the same tests that were used within the last step of the
      Deploy Compute Instances section. Specifically, ping a public IP address or domain from a Compute Instance within the private VLAN (that’s not designated as the router). This ping should now complete successfully, indicating that the network traffic was successfully forwarded through the router to the public internet.

      ping linode.com



      Source link

      How To Navigate Between Views with Vue Router


      The author selected Open Sourcing Mental Illness to receive a donation as part of the Write for DOnations program.

      Introduction

      Most websites or applications have multiple HTML pages that are either static or dynamically generated. With more traditional websites, there is one Document Object Model (DOM) and one page per URL route. With single-page applications however, every “page” or view is rendered within one HTML page. User interface (UI) frameworks like Vue.js render these pages and components when needed through the use of a Virtual DOM. This Virtual DOM is a JavaScript representation of the original DOM and is much easier for the client to update.

      When the user visits a single-page application, that application will compare itself to the Virtual DOM and re-render only the parts of the web page that are changed. This technique prevents the page from flashing white when clicking through links. Let’s say you have three sections on a web page: a header, content, and a footer. When you navigate to another URL, Vue will only render the content area of the application that has changed between pages.

      In Vue.js, you can create several views using the first-party library Vue Router. This router makes an association with a view to a URL. In this tutorial, you are going to learn how to add the Vue Router library, integrate it into your project, and create dynamically generated routes. You will also learn the different types of routes available to you as a developer. When completed, you will have an application that you can navigate using a variety of methods.

      To illustrate these concepts, you’ll create a small application that displays airport information. When the user clicks on an airport card, the application will navigate to a dynamic view of the airport’s details. To do this, the program will read a URL parameter and filter out data based on that parameter.

      Prerequisites

      To complete this tutorial, you will need:

      Step 1 — Setting Up the Sample Application

      The application that you are going to build to learn Vue Router will require some initial data. In this step, you will create and structure this data to be accessible to your Vue app later in the tutorial.

      To add this dataset, you need to create a data directory and create a JavaScript file with the name airports.js. If you are not in the src directory, cd into it first:

      Then make a data directory:

      Now create a data/airports.js file and open it in your text editor.

      Add the following to create data for your application:

      airport-codes/src/data/airports.js

      export default [
        {
          name: 'Cincinnati/Northern Kentucky International Airport',
          abbreviation: 'CVG',
          city: 'Hebron',
          state: 'KY',
            destinations: {
            passenger: [ 'Toronto', 'Seattle/Tacoma', 'Austin', 'Charleston', 'Denver', 'Fort Lauderdale', 'Jacksonville', 'Las Vegas', 'Los Angeles', 'Baltimore', 'Chicago', 'Detroit', 'Dallas', 'Tampa' ],
              cargo: [ 'Anchorage', 'Baltimore', ' Chicago' , 'Indianapolis', 'Phoenix', 'San Francisco', 'Seattle', 'Louisville', 'Memphis' ]
            }
        },
        {
          name: 'Seattle-Tacoma International Airport',
          abbreviation: 'SEA',
          city: 'Seattle',
          state: 'WA',
            destinations: {
            passenger: [ 'Dublin', 'Mexico City', 'Vancouver', 'Albuquerque', 'Atlanta', 'Frankfurt', 'Amsterdam', 'Salt Lake City', 'Tokyo', 'Honolulu' ],
              cargo: [ 'Spokane', 'Chicago', 'Dallas', ' Shanghai', 'Cincinnati', 'Luxenbourg', 'Anchorage', 'Juneau', 'Calgary', 'Ontario' ]
            }
        },
        {
          name: 'Minneapolis-Saint Paul International Airport',
          abbreviation: 'MSP',
          city: 'Bloomington',
          state: 'MN',
            destinations: {
            passenger: [ 'Dublin', 'Paris', 'Punta Cana', 'Winnipeg', 'Tokyo', 'Denver', 'Tulsa', 'Washington DC', 'Orlando', 'Mexico City' ],
              cargo: [ 'Cincinnati', 'Omaha', 'Winnipeg', 'Chicago', 'St. Louis', 'Portland', 'Philadelphia', 'Milwaukee', 'Ontario' ]
            }
        }
      ]
      

      This data is an array of objects consisting of a few airports in the United States. In this application, you are going to iterate through this data to generate cards consisting of the name, abbreviation, city, and state properties. When the user clicks on a card, you will route them to a dynamic view with Vue Router and read from one of these properties. From there, you will create a nested route to display information stored in the destination property.

      Save and exit from the file.

      Next, create a Home.vue component inside of a directory called views. You can create this directory and component by opening your terminal and using the mkdir and touch commands.

      Run the following command to make the directory:

      Then create the Home.vue file:

      This Home.vue component will act as this application’s homepage. In it, you will leverage the v-for directive to iterate through the airports.js dataset and show each airport in a card.

      Add the following code to Home.vue:

      airport-codes/src/views/Home.vue

      <template>
        <div class="wrapper">
          <div v-for="airport in airports" :key="airport.abbreviation" class="airport">
            <p>{{ airport.abbreviation }}</p>
            <p>{{ airport.name }}</p>
            <p>{{ airport.city }}, {{ airport.state }}</p>
          </div>
        </div>
      </template>
      
      <script>
      import { ref } from 'vue'
      import allAirports from '@/data/airports.js'
      
      export default {
        setup() {
          const airports = ref(allAirports)
              return { airports }
          }
      }
      </script>
      
      <style>
      #app {
        font-family: Avenir, Helvetica, Arial, sans-serif;
        -webkit-font-smoothing: antialiased;
        -moz-osx-font-smoothing: grayscale;
        text-align: center;
        color: #2c3e50;
        margin-top: 60px;
      }
      .wrapper {
        display: grid;
        grid-template-columns: 1fr 1fr 1fr;
        grid-column-gap: 1rem;
        max-width: 960px;
        margin: 0 auto;
      }
      .airport {
        border: 3px solid;
        border-radius: .5rem;
        padding: 1rem;
      }
      .airport p:first-child {
        font-weight: bold;
        font-size: 2.5rem;
        margin: 1rem 0;
      }
      .airport p:last-child {
        font-style: italic;
        font-size: .8rem;
      }
      </style>
      

      You may notice that there is some CSS included in this code snippet. In the Home.vue component, you are iterating through a collection of airports, each of which is assigned a CSS class of airport. This CSS adds some styling to the generated HTML by making borders to give each airport the apperance of a card. :first-child and :last-child are pseudo selectors that apply different styling to the first and last p tags in the HTML inside of the div with the class of airport.

      Save and close the file.

      Now that you have this initial view created along with the local dataset, you will install Vue Router in the next step.

      Step 2 — Installing Vue Router

      There are a few ways you can install Vue Router. If you are creating a new project from scratch with the Vue CLI, you can select Vue Router in the prompt; Vue CLI will then install and configure it for you. For the sake of this tutorial, however, it is assumed that you did not select the Vue Router option in the CLI setup. You will instead install Vue Router via npm.

      To install Vue Router, first move from the src directory back to the root of your project directory:

      Then run the following in your terminal window in the root directory of your project:

      You may notice the @next in this command. Since this project is using Vue 3 and the Composition API, you are telling npm to download the latest experimental version of this library. If you would like more information on current releases, check out the Vue Router release page on GitHub.

      This will download the vue-router library from npm and add it to your package.json file, so that it automatically downloads the next time you run npm install.

      The next step is to create your routes file. This file will contain all the possible routes that the user can navigate to. When a certain route is visited in the URL bar, the component that is associated with a URL route will mount.

      In your terminal, in the src directory, move into the src directory and create a router directory:

      Next, create an index.js file inside the router directory:

      Open the file you just created into an editor of your choice. The first thing to do is import the Vue Router library. You actually don’t need to access everything in this library in order to create routes. You can opt to destructure or import only what you need to minimize the bundle size. In this case, you need two functions from vue-router: createWebHistory and createRouter. These functions create a history that a user can go back to and construct a router object for Vue, respectively.

      Add the following code to router/index.js:

      airport-codes/src/router/index.js

      import { createWebHistory, createRouter } from "vue-router"
      

      Next, add the following highlighted lines to create and export your router:

      airport-codes/src/router/index.js

      import { createWebHistory, createRouter } from "vue-router"
      
      const router = createRouter({
        history: createWebHistory(),
      })
      
      export default router
      

      This file will export a router object that is returned from the createRouter function. The object you pass in has two properties: history and routes. The history property contains the generated history from createWebHistory and routes is an array of objects. You will add routes later in this tutorial.

      Next, import the Home view and create an array of objects, storing them into a const called routes:

      airport-codes/src/router/index.js

      import { createWebHistory, createRouter } from "vue-router"
      import Home from "@/views/Home.vue"
      
      const routes = [
        {
          path: "/",
          name: "Home",
          component: Home,
        },
      ]
      
      const router = createRouter({ ... })
      
      export default router
      

      Each route is an object with three properties:

      • path: The URL address
      • name: An assigned name to reference a route in your project
      • component: The component that gets mounted when the path is entered in the URL bar of the browser

      Now that your routes array as been created, you will need to add it to the exported router object.

      After the history key/value pair, add routes. This is shorthand for routes: routes in JavaScript:

      airport-codes/src/router/index.js

      import { createWebHistory, createRouter } from "vue-router"
      
      const routes = [ ... ]
      
      const router = createRouter({
        history: createWebHistory(),
        routes
      })
      
      export default router
      

      At this point, Vue Router is integrated into your project, and you have a route registered. Save and exit the file.

      In another terminal, run the following command to start a development server on your local machine:

      If you visit localhost:8080/ in your browser window, you will not see the Home.vue component just yet. The last step in this integration process is to tell Vue to listen to this router/index.js file and inject the mounted component where <router-view /> is referenced. To do this, you need to reference it in the src/main.js file of your application.

      First, open src/main.js. Then add the following highlighted lines:

      airport-codes/src/main.js

      import { createApp } from 'vue'
      import App from './App.vue'
      import router from './router'
      
      createApp(App).use(router).mount('#app')
      

      With this .use() function that is chained to createApp, Vue.js is now listening to route changes and leveraging your src/router/index.js file. However, Vue Router has no way to display the mounted Home.vue component. To do this, you need to add the router-view component inside your App.vue file. This component tells Vue Router to mount any component associated with a route where <router-view /> is.

      Save and exit the main.js file.

      Next, open the App.vue file. Delete the default contents and replace it with the following:

      airport-codes/src/App.vue

      <template>
        <router-view />
      </template>
      

      Save and exit the file.

      Now visit localhost:8080/ in your browser. You will find the Home.vue component rendered, as shown in the following screenshot:

      Three cards displaying information about airports, retrieved from the data/airports.js file.

      Vue Router has now been downloaded and integrated with a registered route. In the next section, you are going to create additional routes, including two internal pages and a default 404 page if no route was detected.

      Step 3 — Creating Internal Pages

      At this point, your App.vue can render any component configured in your src/router/index.js file. When working with a Vue CLI-generated project, one of the directories that is created for you is views. This directory contains any .vue component that is directly mapped to a route in the router/index.js file. It’s important to note that this isn’t done automatically. You will need to create a .vue and import it into your router file to register it, as detailed earlier.

      Before you have all of your other routes defined, you can create a default route. In this tutorial, this default route will act as a 404 - Not Found page—a fallback in the case no route is found.

      First, create the view that will act as the 404 page. Change into the views directory:

      Then create a file called PageNotFound.vue:

      In your text editor, open this PageNotFound.vue file that you just created. Add the following HTML code to give the view something to render:

      airport-codes/src/views/PageNotFound.vue

      <template>
        <div>
          <h1>404 - Page Not Found</h1>
          <p>This page no longer exists or was moved to another location.</p>
        </div>
      </template>
      

      Save and close the file.

      Now that the PageNotFound.vue component has been created, it’s time to create a catch-all route for your application. Open up the src/router/index.js file in your text editor and add the following highlighted code:

      airport-codes/src/router/index.js

      import { createWebHistory, createRouter } from "vue-router"
      import Home from "@/views/Home.vue"
      import PageNotFound from '@/views/PageNotFound.vue'
      
      const routes = [
        {
          path: "/",
          name: "Home",
          component: Home,
        },
        {
          path: '/:catchAll(.*)*',
          name: "PageNotFound",
          component: PageNotFound,
        },
      ]
      ...
      

      Vue Router for Vue 3 uses a custom RegEx. The value of path contains this new RegEx, which is telling Vue to render PageNotFound.vue for every route, unless the route is already defined. The catchAll in this route refers to a dynamic segment within Vue Router, and (.*) is a regular expression that captures any string.

      Save this file and visit your application in your browser window at localhost:8080/not-found. You will find the PageNotFound.vue component rendered in your browser, as shown in the following image:

      A 404 page that tells the user that the page they are looking for has not been found.

      Feel free to change this URL to anything else; you will get the same result.

      Before moving on, create another route for your application. This route will be an about page.

      Open your terminal of choice and create the file in the views directory:

      In your text editor, open this About.vue file that you just created. Add the following HTML to create more information about your site:

      airport-codes/src/views/About.vue

      <template>
        <div>
          <h1>About</h1>
          <p>This is an about page used to illustrate mapping a view to a router with Vue Router.</p>
        </div>
      </template>
      

      Save and close the file.

      With that view created, open the src/router/index.js file in your text editor, import the About.vue component, and register a new route with a path of /about:

      airport-codes/src/router/index.js

      import { createWebHistory, createRouter } from "vue-router"
      import Home from '@/views/Home.vue'
      import About from '@/views/About.vue'
      import PageNotFound from '@/views/PageNotFound.vue'
      
      ...
      const routes = [
        {
          path: "/",
          name: "Home",
          component: Home,
        },
        {
          path: '/about',
          name: "About",
          component: About,
        },
        {
          path: '/:catchAll(.*)*',
          name: "PageNotFound",
          component: PageNotFound,
        },
      ]
      ...
      

      Save and close the file.

      At this point, you have three different routes:

      • localhost:8080/, which routes to Home.vue
      • localhost:8080/about, which routes to About.vue
      • Any other route, which by default goes to PageNotFound.vue

      Once you save this file, open your browser and first visit localhost:8080/. Once the application loads, you will find the contents of Home.vue: the collection of airport cards.

      Continue testing these routes by visiting localhost:8080/about. This is a static route, so you will find the contents of the About.vue component, which at this point contains a heading and a paragraph.

      A placehold about page for the sample app that reads

      Next, you can test the PageNotFound.vue component by visiting anything else in your browser. For example, if you visit, localhost:8080/some-other-route, Vue Router will default to that catchAll route since that route is not defined.

      As this step illustrates, Vue Router is a handy first-party library that renders a component that is associated with a specific route. In this step, this library was downloaded and integrated globally through the main.js file and was configured in your src/router/index.js file.

      So far, most of your routes are exact routes. This means a component will only mount if the URL fragment matches the path of the router exactly. However, there are other types of routes that have their own purpose and can dynamically generate content. In the next step, you are going to implement the different types of routes and learn when to use one or the other.

      Step 4 — Creating Routes With Parameters

      At this point, you have created two exact routes and a dynamic route to a 404 page. But Vue Router has more than these types of routes. You can use the following routes in Vue Router:

      • Dynamic Routes: Routes with dynamic parameters that your application can reference to load unique data.
      • Named Routes: Routes that can be accessed using the name property. All the routes created at this point have a name property.
      • Nested Routes: Routes with children associated with them.
      • Static or Exact Routes: Routes with a static path value.

      In this section, you will create a dynamic route displaying individual airport information and a nested route for airport destinations.

      Dynamic Routes

      A dynamic route is useful when you want to reuse a view to display different data depending on the route. For example, if you wanted to create a view that displays airport information depending on the airport code in the URL bar, you could use a dynamic route. In this example, if you were to visit a route of localhost:8080/airport/cvg, your application would display data from the airport with the code cvg, the Cincinnati/Northern Kentucky International Airport. Next, you will create this view as described.

      Open up your terminal and create a new .vue file with the touch command. If src is your current working directory, the command will look like this:

      • touch views/AirportDetail.vue

      After that is created, open this file in your text editor of choice. Go ahead and create your template and script tags to set up this component:

      airport-codes/src/views/AirportDetail.vue

      <template>
        <div>
      
        </div>
      </template>
      
      <script>
      export default {
        setup() { }
      }
      </script>
      

      Save and close this file.

      Next, you need to register this view in the src/router/index.js file. Open up this file in your text editor, then add the following highlighted lines:

      airport-codes/src/router/index.js

      import Home from '@/views/Home.vue'
      import About from '@/views/About.vue'
      import AirportDetail from '@/views/AirportDetail.vue'
      import PageNotFound from '@/views/PageNotFound.vue'
      
      ...
      const routes = [
        {
          path: "/",
          name: "Home",
          component: Home,
        },
        {
          path: '/about',
          name: "About",
          component: About,
        },
        {
          path: '/airport/:code',
          name: "AirportDetail",
          component: AirportDetail,
        },
        {
         path: '/:catchAll(.*)*',
         name: "PageNotFound",
         component: PageNotFound,
        },
      ]
      ...
      

      The :code in this new route is called a parameter. A parameter is any value that can be accessed in your application via this name. In this case, you have a parameter named code. Next, you will display the information associated with this abbreviation by leveraging this parameter.

      Save and close this file.

      Now that you have some data, open the AirportDetail.vue component again. Import the airport data in this component:

      airport-codes/src/views/AirportDetail.vue

      ...
      <script>
      import airports from '@/data/airports.js'
      
      export default {
        setup() { }
      }
      </script>
      

      Next, create a computed property that returns one object from the array if the abbreviation property of that object matches the :code parameter in the URL. In Vue 3, you need to destructure computed from the vue library:

      airport-codes/src/views/AirportDetail.vue

      ...
      <script>
      import { computed } from 'vue'
      import { useRoute } from 'vue-router'
      import airports from '@/data/airports.js'
      
      export default {
        setup() {
        const route = useRoute()
          const airport = computed(() => {
              return airports.filter(a => a.abbreviation === route.params.code.toUpperCase())[0]
          })
      
          return { airport }
        }
      }
      </script>
      

      This computed property uses the filter array method in JavaScript to return an array of objects if the condition is met. Since you only want one object, the code will always return the first object, which it will access with the [0] index syntax. The route.params.code is how you access the parameter that was defined in your router file. In order to access the route’s properties, you will need to import a function from vue-router named useRoute. Now when visiting a route, you have immediate access to all of the route’s properties. You are using dot notation to access this code param and retrieve its value.

      At this point, this computed property will return a single airport object if the code in the URL matches the abbreviation property. This means that you have access to all of the object properties of an airport and can construct the template of this component.

      Continue editing the AirportDetail.vue file in your text editor and build out your template to display the information of the airport:

      airport-codes/src/views/AirportDetail.vue

      <template>
       <div>
         <p>{{ airport.name }} ({{ airport.abbreviation }})</p>
         <p>Located in {{ airport.city }}, {{ airport.state }}</p>
       </div>
      </template>
      ...
      

      Open your browser and visit localhost:8080/airport/sea. Information related to the Seattle-Tacoma International Airport will render in your browser, as shown in the following image:

      Informational page about the Seattle-Tacoma International Airport, including its abbreviation and location.

      Nested Routes

      As your application grows, you may find that you have a number of routes that are related to a parent. A good illustration of this could be a series of routes associated with a user. If there is a user named foo, you could have multiple nested routes for the same user, each starting with /user/foo/. When the user is on the /user/foo/profile route, the user would be viewing a profile page associated with them. A posts page for the user might have a route of /user/foo/posts. This nesting can give you the ability to organize your routes along with your components. For more information on this, check out the Vue Router documentation.

      You can apply this same pattern to the application you’ve built throughout this tutorial. In this section, you’re going to add a view to display the destinations that each airport supports.

      Open your terminal and, in the src directory, create a new file with the name AirportDestinations.vue:

      • touch views/AirportDestinations.vue

      Next, open your text editor and add the following:

      airport-codes/src/views/AirportDestinations.vue

      <template>
              <h1>Destinations for {{ airport.name }} ({{ airport.abbreviation }}</h1>
              <h2>Passenger</h2>
              <ul>
                  <li v-for="(destination, i) in airport.destinations.passenger" :key="i">
                      {{ destination }}
                  </li>
              </ul>
          <h2>Cargo</h2>
          <ul>
                  <li v-for="(destination, i) in airport.destinations.cargo" :key="i">
                      {{ destination }}
                  </li>
          </ul>
      </template>
      
      <script>
          import { computed } from 'vue'
        import { useRoute } from 'vue-router'
        import airports from '@/data/airports.js'
      
          export default {
              setup() {
            const route = useRoute()
                  const airport = computed(() => {
                      return airports.filter(a => a.abbreviation === route.params.code.toUpperCase())[0]
                  })
                  return { airport }
              }
          }
      </script>
      

      This view will render all of the destinations for each airport from the airports.js file. In this view, you are using the v-for directive to iterate through the destinations. Much like the AirportDetail.vue view, you are creating a computed property called airport to get the airport that matches the :code parameter in the URL bar.

      Save and close the file.

      To create a nested route, you need to add the children property to your route object in the src/router/index.js file. The child (nested) route object contains the same properties as its parent.

      Open up router/index.js and add the following highlighted lines:

      airport-codes/src/router/index.js

      import Home from '@/views/Home.vue'
      import About from '@/views/About.vue'
      import AirportDetail from '@/views/AirportDetail.vue'
      import AirportDestinations from '@/views/AirportDestinations.vue'
      import PageNotFound from '@/views/PageNotFound.vue'
      
      ...
      const routes = [
          ...
        {
          path: '/airport/:code',
          name: "AirportDetail",
          component: AirportDetail,
              children: [
                  {
                    path: 'destinations',
                      name: 'AirportDestinations',
                      component: AirportDestinations
                  }
              ]
        },
          ...
      ]
      ...
      

      The path for this child route is short compared to its parent. That is because, with nested routes, you do not need to add the entire route. The child inherits its parent’s path and will prepend it to the child path.

      Open your browser window, and visit localhost:8080/airport/msp/destinations. Nothing appears to be different from the parent, AirportDetails.vue. That is because when using nested routes, you need to include the <router-view /> component in the parent. When visiting the child route, Vue will inject the content from the child view into the parent:

      airport-codes/src/views/AirportDetail.vue

      <template>
       <div>
         <p>{{ airport.name }} ({{ airport.abbreviation }})</p>
         <p>Located in {{ airport.city }}, {{ airport.state }}</p>
           <router-view />
       </div>
      </template>
      

      In this case, when visiting the destinations route in the browser, AirportDestinations.vue will display an unordered list of the destinations that the Minneapolis-Saint Paul International Airport supports, for both passenger and cargo flights.

      A page rendered with a list of passenger and cargo destinations for Minneapolis-Saint Paul International Airport.

      In this step, you created dynamic and nested routes and learned how to use a computed property that checks against the :code parameter in the URL bar. Now that all the routes have been created, in the final step, you are going to navigate between the different types of routes by creating links with the <router-link /> component.

      Step 5 — Navigating Between Routes

      When working with single-page applications, there are a few caveats you need to be aware off. Since every page is bootstrapped into a single HTML page, navigating between internal routes using the standard anchor (<a />) will not work. Instead, you will need to use the <router-link /> component provided by Vue Router.

      Unlike the standard anchor tag, router-link provides a number of different ways to link to other routes in your application, including using named routes. Named routes are routes that have a name property associated with them. Every link that you have created up to this point has a name already associated with it. Later in this step, you’ll learn how to navigate using name rather than the path.

      The <router-link /> component was globally imported when you initially integrated Vue Router into your application. To use this component, you will add it to your template and provide it with a prop value.

      Open the Home.vue component within the views directory in your editor. This view displays every airport that you have in the airports.js file. You can use router-link to replace the containing div tag in order to make each card clickable to their respective detail view.

      Add the following highlighted lines to Home.vue:

      airport-codes/src/views/Home.vue

      <template>
          <div class="wrapper">
              <router-link v-for="airport in airports" :key="airport.abbreviation" class="airport">
                  <p>{{ airport.abbreviation }}</p>
                  <p>{{ airport.name }}</p>
                  <p>{{ airport.city }}, {{ airport.state }}</p>
              </router-link>
          </div>
      </template>
      ...
      

      At the bare minimum, router-link requires one prop called to. This to prop accepts an object with a number of key/value pairs. Since this card uses the v-for directive, these cards are data driven. You’ll need to be able to navigate to the AirportDetail.vue component. If you review the path for that component, it is accessible via /airports/:code.

      When navigating via the path property, it is a one-to-one match. Add the following highlighted segments:

      airport-codes/src/views/Home.vue

      <template>
          <div class="wrapper">
              <router-link :to="{ path: `/airports/${airport.abbreviation}` }" v-for="airport in airports" :key="airport.abbreviation" class="airport">
                  ...
              </router-link>
          </div>
      </template>
      ...
      

      In this code, you are using JavaScript string interpolation to insert the airport code dynamically as the value for the path property. However, as your applications grows in scale, you may find that navigating to a different route via the path is not the best method. In fact, it is generally considered best practice to navigate using named routes.

      To implement a named route, use the name of the route over the path. If you reference src/router/index.js you will find that AirportDetail.vue has a name of AirportDetail:

      airport-codes/src/views/Home.vue

      <template>
          <div class="wrapper">
              <router-link :to="{ name: 'AirportDetail' }" v-for="airport in airports" :key="airport.abbreviation" class="airport">
                  ...
              </router-link>
          </div>
      </template>
      ...
      

      The benefit of using named routes over exact routes is that named routes are not dependent on the path of the route. URL structures will always change in development, but the name of the route will seldom change.

      You might notice that you cannot pass in the airport code as you did earlier with the exact route. If you need to pass in parameters into a named route, you will need to add the params property containing an object that represents your parameter:

      airport-codes/src/views/Home.vue

      <template>
          <div class="wrapper">
              <router-link :to="{ name: 'AirportDetail', params: { code: airport.abbreviation } }" v-for="airport in airports" :key="airport.abbreviation" class="airport">
                  ...
              </router-link>
          </div>
      </template>
      ...
      

      Save this file and view it in your browser at localhost:8080. Clicking on the Seattle-Tacoma airport card will now navigate you to localhost:8080/airport/sea.

      Programmatic Navigation

      The <router-link /> component is great to use when you need to navigate to another view within your HTML template. But what about those cases when you need to navigate between routes within a JavaScript function? Vue Router offers a solution to this problem called programmatic navigation.

      Earlier in this tutorial, you created a catch-all route called PageNotFound. It would be a good user experience to always navigate to that page if the airport computed property returned undefined in the AirportDetail.vue and AirportDestinations.vue components. In this section, you will implement this feature.

      In your text editor, open the AirportDetail.vue component. To achieve this, detect if airport.value is undefined. This function will be called when the component is first mounted, which means you will need to use Vue’s lifecycle methods.

      Add the following highlighted lines:

      airport-codes/src/views/AirportDetail.vue

      <template>
        <div v-if="airport">
         <p>{{ airport.name }} ({{ airport.abbreviation }})</p>
         <p>Located in {{ airport.city }}, {{ airport.state }}</p>
         <router-view />
        </div>
      </template>
      
      <script>
          import { computed, onMounted } from 'vue'
          import { useRoute } from 'vue-router'
          import airports from '@/data/airports.js'
          import router from '@/router'
      
          export default {
                  setup() {
              const route = useRoute()
                      const airport = computed(() => {
                          return airports.filter(a => a.abbreviation === route.params.code.toUpperCase())[0]
                      })
      
                      onMounted(() => {
                          if (!airport.value) {
                              // Navigate to 404 page here
                          }
                      })
      
                      return { airport }
                  }
          }
      </script>
      

      In this onMounted function, you are checking if airport.value is a falsy value. If it is, then you route to PageNotFound. You can handle programmatic routing similarly to how you handled the router-link component. Since you cannot use a component in JavaScript functions, you need to use router.push({ ... }). This push method of the router object accepts the value of the to prop when using the link component:

      /src/views/AirportDetail.vue

      <script>
          ...
        onMounted(() => {
              if (!airport.value) {
                  router.push({ name: 'PageNotFound' })
              }
        })
          ...
      </script>
      

      If the route doesn’t exist, or if the data is not returned properly, this will protect the user from the broken web page.

      Save the file and navigate to localhost:8080/airport/ms. Since ms is not an airport code in the data, the airport object will be undefined, and the router will redirect you to the 404 page.

      Conclusion

      In this tutorial, you used Vue Router to create a web application that routes between different views. You learned the different types of routes, including exact, named, and nested, as well as created links with parameters using the router-link component. In the final step, you provided programmatic navigation using JavaScript by leveraging the router’s push() function.

      For more information on Vue Router, it’s recommended to read through their documentation. The CLI tool specifically has many additional features that weren’t covered in this tutorial. For more tutorials on Vue, check out the Vue Topic Page.



      Source link

      How To Handle Routing in React Apps with React Router


      The author selected Creative Commons to receive a donation as part of the Write for DOnations program.

      Introduction

      In React, routers help create and navigate between the different URLs that make up your web application. They allow your user to move between the components of your app while preserving user state, and can provide unique URLs for these components to make them more shareable. With routers, you can improve your app’s user experience by simplifying site navigation.

      React Router is one of the most popular routing frameworks for React. The library is designed with intuitive components to let you build a declarative routing system for your application. This means that you can declare exactly which of your components has a certain route. With declarative routing, you can create intuitive routes that are human-readable, making it easier to manage your application architecture.

      In this tutorial, you’ll install and configure React Router, build a set of routes, and connect to them using the <Link> component. You’ll also build dynamic routes that collect data from a URL that you can access in your component. Finally, you’ll use Hooks to access data and other routing information and create nested routes that live inside components that are rendered by parent routes.

      By the end of this tutorial, you’ll be able to add routes to any React project and read information from your routes so that you can create flexible components that respond to URL data.

      Prerequisites

      Step 1 — Installing React Router

      In this step, you’ll install React Router into your base project. In this project, you are going to make a small website about marine mammals. Each mammal will need a separate component that you’ll render with the router. After installing the library, you’ll create a series of components for each mammal. By the end of this step, you’ll have a foundation for rendering different mammals based on route.

      To start, install the React Router package. There are two different versions: a web version and a native version for use with React Native. You will install the web version.

      In your terminal, use npm to install the package:

      • npm install react-router-dom

      The package will install and you’ll receive a message such as this one when the installation is complete. Your message may vary slightly:

      Output

      ... + [email protected] added 11 packages from 6 contributors and audited 1981 packages in 24.897s 114 packages are looking for funding run `npm fund` for details found 0 vulnerabilities

      You now have the package installed. For the remainder of this step, you’ll create a series of components that will each have a unique route.

      To start, make a directory for three different mammals: manatees, narwhals, and whales. Run the following commands:

      • mkdir src/components/Manatee
      • mkdir src/components/Narwhal
      • mkdir src/components/Whale

      Next, create a component for each animal. Add an <h2> tag for each mammal. In a full application, the child components can be as complex as you want. They can even import and render their own child components. For this tutorial, you’ll render only the <h2> tag.

      Begin with the manatee component. Open Manatee.js in your text editor:

      • nano src/components/Manatee/Manatee.js

      Then add the basic component:

      router-tutorial/src/components/Manatee/Manatee.js

      import React from 'react';
      
      export default function Manatee() {
        return <h2>Manatee</h2>;
      }
      

      Save and close the file.

      Next, create a component for the narwhal:

      • nano src/components/Narwhal/Narwhal.js

      Add the same basic component, changing the <h2> to Narwhal:

      router-tutorial/src/components/Narwhal/Narwhal.js

      import React from 'react';
      
      export default function Narwhal() {
        return <h2>Narwhal</h2>;
      }
      

      Save and close the file.

      Finally, create a file for Whale:

      • nano src/components/Whale/Whale.js

      Add the same basic component, changing the <h2> to Whale:

      router-tutorial/src/components/Whale/Whale.js

      import React from 'react';
      
      export default function Whale() {
        return <h2>Whale</h2>;
      }
      

      Save and close the file. In the next step, you’ll start connecting routes; for now, render the basic component in your application.

      Open App.js:

      • nano src/components/App/App.js

      Add an <h1> tag with the name of the website (Marine Mammals) inside of a <div> with a className of wrapper. This will serve as a template. The wrapper and <h1> tag will render on every page. In full applications, you might add a navigation bar or a header component that you’d want on every page.

      Add the following highlighted lines to the file:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import './App.css';
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
          </div>
        );
      }
      
      export default App;
      

      Next, import Manatee and render inside the <div>. This will serve as a placeholder until you add more routes:

      router-tutorial/src/components/App/App.js

      
      import React from 'react';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <Manatee />
          </div>
        );
      }
      
      export default App;
      

      Save and close the file.

      Now that you have all of the components, add some padding to give the application a little space.

      Open App.css:

      • nano src/components/App/App.css

      Then replace the contents with the following code that adds a padding of 20px to the .wrapper class:

      router-tutorial/src/components/App/App.css

      .wrapper {
          padding: 20px;
      }
      

      Save and close the file. When you do, the browser will refresh to show your basic component:

      Marine Mammals

      Now you have a basic root component that you will use to display other components. If you didn’t have a router, you could conditionally display components using the useState Hook. But this wouldn’t offer a great experience for your users. Anytime a user refreshes the page, the user’s selection would disappear. Further, they wouldn’t be able to bookmark or share specific states of the application. A router will solve all these problems. The router will preserve the user state and will give the user a clear URL that they can save or send to others.

      In this step, you installed React Router and created basic components. The components are going to be individual pages that you’ll display by route. In the next step, you’ll add routes and use the <Link> component to create performant hyperlinks.

      Step 2 — Adding Routes

      In this step, you’ll create a base router with individual routes for each page. You’ll order your routes to ensure that components are rendered correctly and you’ll use the <Link> component to add hyperlinks to your project that won’t trigger a page refresh.

      By the end of this step, you’ll have an application with a navigation that will display your components by route.

      React Router is a declarative routing framework. That means that you will configure the routes using standard React components. There are a few advantages to this approach. First, it follows the standard declaractive nature of React code. You don’t need to add a lot of code in componentDidMount methods or inside a useEffect Hook; your routes are components. Second, you can intuitively place routes inside of a component with other components serving as a template. As you read the code, you’ll find exactly where the dynamic components will fit in relation to the global views such as navigation or footers.

      To start adding routes, open App.js:

      • nano src/components/App/App.js

      The <h1> tag is going to serve as a global page title. Since you want it to appear on every page, configure the router after the tag.

      Import BrowserRouter, Route, and Switch from react-router-dom. BrowserRouter will be the base configuration. Switch will wrap the dynamic routes and the Route component will configure specific routes and wrap the component that should render:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <Manatee />
          </div>
        );
      }
      
      export default App;
      

      Add the BrowserRouter component to create a base router. Anything outside of this component will render on every page, so place it after your <h1> tag. In addition, if you have site-wide context that you want to use, or some other store such as Redux, place those components outside the router. This will make them available to all components on any route:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <Manatee />
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Next, add the Switch component inside BrowserRouter. This component will activate the correct route, much like the JavaScript switch statement. Inside of Switch, add a Route component for each route. In this case, you’ll want the following routes: /manataee, /narwhal, and /whale. The Route component will take a path as a parameter and surround a child component. The child component will display when the route is active.

      Create a route for the path / and render the Manatee component:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <Switch>
                <Route path="/">
                  <Manatee />
                </Route>
              </Switch>
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Save the file. When you do the browser will reload and you’ll find the information for the manatee component:

      Manatee showing at route /

      If you try a different route such as http://localhost:3000/whale, you’ll still find the manatee component.

      Manatee on /whale route

      The Switch component will render the first route that matches that pattern. Any route will match /, so it will render on every page. That also means that order is important. Since the router will exit as soon as it finds a match, always put a more specific route before a less specific route. In other words, /whale would go before / and /whale/beluga would go before /whale.

      If you want the route to match only the route as written and not any child routes, you can add the exact prop. For example, <Route exact path="https://www.digitalocean.com/manatee"> would match /manatee, but not /manatee/african.

      Update the route for the Manatee component to /manatee, then import the remaining components and create a route for each:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      import Narwhal from '../Narwhal/Narwhal';
      import Whale from '../Whale/Whale';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <Switch>
                <Route path="https://www.digitalocean.com/manatee">
                  <Manatee />
                </Route>
                <Route path="https://www.digitalocean.com/narwhal">
                  <Narwhal />
                </Route>
                <Route path="https://www.digitalocean.com/whale">
                  <Whale />
                </Route>
              </Switch>
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Save the file. When you do, the browser will refresh. If you visit http://localhost:3000/, only the <h1> tag will render, because no routes match any of the Route components:

      No component on /

      If you visit http://localhost:3000/whale, you’ll find the Whale component:

      Whale on /whale route

      Now that you have some components, create navigation for a user to move between pages.

      Use the <nav> element to denote that you are creating a navigation portion of the page. Then add an unordered list (<ul>) with a list item (<li>) and a hyperlink (<a>) for each mammal:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      import Narwhal from '../Narwhal/Narwhal';
      import Whale from '../Whale/Whale';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <nav>
              <ul>
                <li><a href="https://www.digitalocean.com/manatee">Manatee</a></li>
                <li><a href="https://www.digitalocean.com/narwhal">Narwhal</a></li>
                <li><a href="https://www.digitalocean.com/whale">Whale</a></li>
              </ul>
            </nav>
            <BrowserRouter>
              ...
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Save the file. When you do, the browser will refresh, but there will be a problem. Since you are using the native browser links—<a> tags—you will get the default browser behavior any time you click on a link. That means any time you click on a link, you’ll trigger a full page refresh.

      Notice that the network will reload all of the JavaScript files when you click a link. That’s a big performance cost for your users.

      Browser refresh on link click

      At this point, you could add a click event handler on each link and prevent the default action. That would be a lot of work. Instead, React Router has a special component called Link that will handle the work for you. It will create a link tag, but prevent the default brower behavior while pushing the new location.

      In App.js, import Link from react-router-dom. Then replace each <a> with a Link. You’ll also need to change the href attribute to the to prop.

      Finally, move the <nav> component inside of the BrowserRouter. This ensures that the Link component is controlled by react-router:

      router-tutorial/src/components/App/App.js

      
      import React from 'react';
      import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      import Narwhal from '../Narwhal/Narwhal';
      import Whale from '../Whale/Whale';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <nav>
                <ul>
                  <li><Link to="https://www.digitalocean.com/manatee">Manatee</Link></li>
                  <li><Link to="https://www.digitalocean.com/narwhal">Narwhal</Link></li>
                  <li><Link to="https://www.digitalocean.com/whale">Whale</Link></li>
                </ul>
              </nav>
              <Switch>
                <Route path="https://www.digitalocean.com/manatee">
                  <Manatee />
                </Route>
                <Route path="https://www.digitalocean.com/narwhal">
                  <Narwhal />
                </Route>
                <Route path="https://www.digitalocean.com/whale">
                  <Whale />
                </Route>
              </Switch>
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Save the file. When you do, the browser will refresh. When you click links, the page will not refresh and the browser will not reload the JavaScript code:

      No refresh on link click

      In this step you added React Router to your current project. You created a route for each component and you added a navigation using the Link component to switch between routes without a page refresh.

      In the next step, you’ll add more complex routes that render different components using URL parameters.

      Step 3 — Accessing Route Data with Hooks

      In this step, you’ll use URL queries and parameters to create dynamic routes. You’ll learn how to pull information from search parameters with the useLocation Hook and how to read information from dynamic URLs using the useParams Hook.

      By the end of this step, you’ll know how to access route information inside of your components and how you can use that information to dynamically load components.

      Suppose you wanted to add another level to your marine mammal application. There are many types of whales, and you could display information about each one. You have two choices of how to accomplish this: You could use the current route and add a specific whale type with search parameters, such as ?type=beluga. You could also create a new route that includes the specific name after the base URL, such as /whale/beluga. This tutorial will start with search parameters, since they are flexible and can handle multiple, different queries.

      First, make new components for different whale species.

      Open a new file Beluga.js in your text editor:

      • nano src/components/Whale/Beluga.js

      Add an <h3> tag with the name Beluga:

      router-tutorial/src/components/Whale/Beluga.js

      import React from 'react';
      
      export default function Beluga() {
        return(
          <h3>Beluga</h3>
        );
      }
      

      Do the same thing for a blue whale. Open a new file Blue.js in your text editor:

      • nano src/components/Whale/Blue.js

      Add an <h3> tag with the name Blue:

      router-tutorial/src/components/Whale/Blue.js

      import React from 'react';
      
      export default function Blue() {
        return(
          <h3>Blue</h3>
        );
      }
      

      Save and close the file.

      Passing Additional Information with Search Parameters

      Next, you are going to pass the whale information as a search parameter. This will let you pass information without needing to create a new URL.

      Open App.js so you can add new links:

      • nano src/components/App/App.js

      Add two new links, one to /whale?type=beluga and one for /whale?type=blue:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      import Narwhal from '../Narwhal/Narwhal';
      import Whale from '../Whale/Whale';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <nav>
                <ul>
                  <li><Link to="https://www.digitalocean.com/manatee">Manatee</Link></li>
                  <li><Link to="https://www.digitalocean.com/narwhal">Narwhal</Link></li>
                  <li><Link to="https://www.digitalocean.com/whale">Whale</Link></li>
                  <li><Link to="/whale?type=beluga">Beluga Whale</Link></li>
                  <li><Link to="/whale?type=blue">Blue Whale</Link></li>
                </ul>
              </nav>
              <Switch>
                ...
              </Switch>
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Save and close the file.

      If you click on the links, you’ll still see the regular whale page. This shows that the standard route is still working correctly:

      Beluga router with whale page

      Since you are correctly rendering the Whale component, you’ll need to update the component to pull the search query out of the URL and use it to render the correct child component.

      Open Whale.js:

      • nano src/components/Whale/Whale.js

      First, import the Beluga and Blue components. Next, import a Hook called useLocation from react-router-dom:

      router-tutorial/src/components/Whale/Whale.js

      import React from 'react';
      import { useLocation } from 'react-router-dom';
      import Beluga from './Beluga';
      import Blue from './Blue';
      
      export default function Whale() {
        return <h2>Whale</h2>;
      }
      

      The useLocation Hook pulls the location information from your page. This is not unique to React Router. The location object is a standard object on all browsers. If you open your browser console and type window.location, you’ll get an object with information about your URL.

      Window location in console

      Notice that the location information includes search, but also includes other information, such as the pathname and the full href. The useLocation Hook will provide this information for you. Inside of Whale.js, call the useLocation Hook. Destructure the result to pull out the search field. This will be a parameter string, such as ?type=beluga:

      router-tutorial/src/components/Whale/Whale.js

      import React from 'react';
      import { useLocation } from 'react-router-dom';
      import Beluga from './Beluga';
      import Blue from './Blue';
      
      export default function Whale() {
        const { search } = useLocation();
        return <h2>Whale</h2>;
      }
      

      There are a number of libraries, such as query-string, that can parse the search for you and convert it into an object that is easier to read and update. In this example, you can use a regular expression to pull out the information about the whale type.

      Use the .match method on the search string to pull out the type: search.match(/type=(.*)/). The parentheses inside the regular expression will capture the match into a results array. The first item in the array is the full match: type=beluga. The second item is the information from the parentheses: beluga.

      Use the data from the .match method to render the correct child component:

      router-tutorial/src/components/Whale/Whale.js

      
      import React from 'react';
      import { useLocation } from 'react-router-dom';
      import Beluga from './Beluga';
      import Blue from './Blue';
      
      export default function Whale() {
        const { search } = useLocation();
        const match = search.match(/type=(.*)/);
        const type = match?.[1];
      
        return (
          <>
            <h2>Whale</h2>
            {type === 'beluga' && <Beluga />}
            {type === 'blue' && <Blue />}
          </>
        );
      }
      

      The symbol ?. is called optional chaining. If the value exists, it returns the value. Otherwise, it will return undefined. This will protect your component in instances where the search parameter is empty.

      Save the file. When you do, the browser will refresh and will render different whales:

      Different whales with search params

      Accessing URL Parameters

      Search parameters work, but they aren’t the best solution in this case. Generally, you’d use search parameters to refine a page: toggling information or loading specific data. In this case, you are not refining a page; you are creating a new static page. Fortunately, React Router provides a way to create dynamic URLs that preserve variable data called URL Parameters.

      Open App.js:

      • nano src/components/App/App.js

      Instead of passing the whale information as a search, you will add it directly to the URL itself. That means that you will move the seach into the URL instead of adding it after a ?. For example, the query/whale?type=blue will now be /whale/blue:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      import Narwhal from '../Narwhal/Narwhal';
      import Whale from '../Whale/Whale';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <nav>
                <ul>
                  <li><Link to="https://www.digitalocean.com/manatee">Manatee</Link></li>
                  <li><Link to="https://www.digitalocean.com/narwhal">Narwhal</Link></li>
                  <li><Link to="https://www.digitalocean.com/whale">Whale</Link></li>
                  <li><Link to="/whale/beluga">Beluga Whale</Link></li>
                  <li><Link to="/whale/blue">Blue Whale</Link></li>
                </ul>
              </nav>
              <Switch>
                <Route path="https://www.digitalocean.com/manatee">
                  <Manatee />
                </Route>
                <Route path="https://www.digitalocean.com/narwhal">
                  <Narwhal />
                </Route>
                <Route path="https://www.digitalocean.com/whale">
                  <Whale />
                </Route>
              </Switch>
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Now you need to create a new route that can capture both /whale/beluga and /whale/blue. You could add them by hand, but this wouldn’t work in situations where you don’t know all the possibilities ahead of time, such as when you have a list of users or other dynamic data.

      Instead of making a route for each one, add a URL param to the current path. The URL param is a keyword prefaced with a colon. React Router will use the parameter as a wildcard and will match any route that contains that pattern.

      In this case, create a keyword of :type. The full path will be /whale/:type. This will match any route that starts with /whale and it will save the variable information inside a parameter variable called type. This route will not match /whale, since that does not contain an additional parameter.

      You can either add /whale as a route after the new route or you can add it before the route of /whale/:type with the exact keyword.

      Add a new route of /whale/:type and add an exact property to the current route:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      import Narwhal from '../Narwhal/Narwhal';
      import Whale from '../Whale/Whale';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <nav>
                <ul>
                  <li><Link to="https://www.digitalocean.com/manatee">Manatee</Link></li>
                  <li><Link to="https://www.digitalocean.com/narwhal">Narwhal</Link></li>
                  <li><Link to="https://www.digitalocean.com/whale">Whale</Link></li>
                  <li><Link to="/whale/beluga">Beluga Whale</Link></li>
                  <li><Link to="/whale/blue">Blue Whale</Link></li>
                </ul>
              </nav>
              <Switch>
                <Route path="https://www.digitalocean.com/manatee">
                  <Manatee />
                </Route>
                <Route path="https://www.digitalocean.com/narwhal">
                  <Narwhal />
                </Route>
                <Route exact path="https://www.digitalocean.com/whale">
                  <Whale />
                </Route>
                <Route path="/whale/:type">
                  <Whale />
                </Route>
              </Switch>
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Save and close the file. Now that you are passing new information, you need to access it and use the information to render dynamic components.

      Open Whale.js:

      • nano src/components/Whale/Whale.js

      Import the useParams Hook. This will connect to your router and pull out any URL parameters into an object. Destructure the object to pull out the type field. Remove the code for parsing the search and use the parameter to conditionally render child components:

      router-tutorial/src/components/Whale/Whale.js

      import React from 'react';
      import { useParams } from 'react-router-dom';
      import Beluga from './Beluga';
      import Blue from './Blue';
      
      export default function Whale() {
        const { type } = useParams();
      
        return (
          <>
            <h2>Whale</h2>
            {type === 'beluga' && <Beluga />}
            {type === 'blue' && <Blue />}
          </>
        );
      }
      

      Save and close the file. When you do, the browser will refresh and you’ll be able to use the new URLs, such as http://localhost:3000/whale/beluga:

      Beluga whale parameter

      URL parameters are a clear way to pass conditional data. They are not as flexible as search parameters, which can be combined or reordered, but they are more clear and easier for search engines to index.

      In this step you passed variable data using search parameters and URL parameters. You also used the useLocation and useParams Hooks to pull information out and to render conditional components.

      But there is one problem: The list of routes is getting long and you are starting to get near duplicates with the /whale and /whale/:type routes. React Router lets you split out child routes directly in the component, which means you don’t need to have the whole list in a single component. In the next step, you’ll render routes directly inside of child components.

      Step 4 — Nesting Routes

      Routes can grow and become more complex. React Router uses nested routes to render more specific routing information inside of child components. In this step, you’ll use nested routes and add routes in different components. By the end of this step, you’ll have different options for rendering your information.

      In the last step, you added routes inside of App.js. This has some advantages: It keeps all routes in one place, essentially creating a site map for your application. But it can easily grow and be difficult to read and maintain. Nested routes group your routing information directly in the components that will render other components, giving you the ability to create mini-templates throughout your application.

      Open App.js:

      • nano src/components/App/App.js

      Remove the /whale/:type route and remove the exact prop so you only have a whale route:

      router-tutorial/src/components/App/App.js

      import React from 'react';
      import { BrowserRouter, Link, Route, Switch } from 'react-router-dom';
      import './App.css';
      
      import Manatee from '../Manatee/Manatee';
      import Narwhal from '../Narwhal/Narwhal';
      import Whale from '../Whale/Whale';
      
      function App() {
        return (
          <div className="wrapper">
            <h1>Marine Mammals</h1>
            <BrowserRouter>
              <nav>
                <ul>
                  <li><Link to="https://www.digitalocean.com/manatee">Manatee</Link></li>
                  <li><Link to="https://www.digitalocean.com/narwhal">Narwhal</Link></li>
                  <li><Link to="https://www.digitalocean.com/whale">Whale</Link></li>
                  <li><Link to="/whale/beluga">Beluga Whale</Link></li>
                  <li><Link to="/whale/blue">Blue Whale</Link></li>
                </ul>
              </nav>
              <Switch>
                <Route path="https://www.digitalocean.com/manatee">
                  <Manatee />
                </Route>
                <Route path="https://www.digitalocean.com/narwhal">
                  <Narwhal />
                </Route>
                <Route path="https://www.digitalocean.com/whale">
                  <Whale />
                </Route>
              </Switch>
            </BrowserRouter>
          </div>
        );
      }
      
      export default App;
      

      Save and close the file.

      Next, open Whale.js. This is where you will add the nested route.

      • nano src/components/Whale/Whale.js

      You will need to do two things. First, get the current path with the useRouteMatch Hook. Next, render the new <Switch> and <Route> components to display the correct components.

      Import useRouteMatch. This will return an object that contains the path and the url. Destructure the object to get the path. You’ll use this as the basis for your new routes:

      router-tutorial/src/components/Whale/Whale.js

      import React from 'react';
      import { useRouteMatch } from 'react-router-dom';
      import Beluga from './Beluga';
      import Blue from './Blue';
      
      export default function Whale() {
        const { path } = useRouteMatch();
      
        return (
          <>
            <h2>Whale</h2>
            {type === 'beluga' && <Beluga />}
            {type === 'blue' && <Blue />}
          </>
        );
      }
      

      Next, import Switch and Route so you can add in new routes. Your new routes will be the same as you made in App.js, but you do not need to wrap them with BrowserRouter. Add the new routes, but prefix the route with the path. The new component will render exactly where you place them, so add the new routes after the <h2>:

      router-tutorial/src/components/Whale/Whale.js

      import React from 'react';
      import { Switch, Route, useRouteMatch } from 'react-router-dom';
      import Beluga from './Beluga';
      import Blue from './Blue';
      
      export default function Whale() {
        const { path } = useRouteMatch();
        return (
          <>
            <h2>Whale</h2>
            <Switch>
              <Route path={`${path}/beluga`}>
                <Beluga />
              </Route>
              <Route path={`${path}/blue`}>
                <Blue />
              </Route>
            </Switch>
          </>
        );
      }
      

      Save the file. When you do, the browser will refresh and you’ll be able to visit the child routes.

      Visiting child routes

      This is a little extra code, but it keeps the child routes situated with their parent. Not all projects use nested routes: some prefer having an explicit list. It is a matter of team preference and consistency. Choose the option that is best for your project, and you can always refactor later.

      In this step, you added nested routes to your project. You pulled out the current path with the useRouteMatch Hook and added new routes in a component to render the new components inside of a base component.

      Conclusion

      React Router is an important part of any React project. When you build single page applications, you’ll use routes to separate your application into usable pieces that users can access easily and consistently.

      As you start to separate your components into routes, you’ll be able to take advantage of code splitting, preserving state via query parameters, and other tools to improve the user experience.

      If you would like to read more React tutorials, check out our React Topic page, or return to the How To Code in React.js series page.



      Source link