<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Donna Brown</title>
    <description>The latest articles on DEV Community by Donna Brown (@dbrownsoftware).</description>
    <link>https://dev.to/dbrownsoftware</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F227376%2Fbd2b05cd-be8d-4b57-ba65-789ea6cb016e.jpeg</url>
      <title>DEV Community: Donna Brown</title>
      <link>https://dev.to/dbrownsoftware</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dbrownsoftware"/>
    <language>en</language>
    <item>
      <title>How to Build a Developer Blog using Next JS 13 and Contentlayer - Part three</title>
      <dc:creator>Donna Brown</dc:creator>
      <pubDate>Wed, 06 Sep 2023 22:35:57 +0000</pubDate>
      <link>https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-three-1155</link>
      <guid>https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-three-1155</guid>
      <description>&lt;p&gt;&lt;strong&gt;Add more directories and pages&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make directories: about, blog, projects under the app directory.  Create a page.tsx in each directory. Since the layout is not changing you don’t need to have a layout.tsx file in each directory. Placeholder text is included for app/about/page.tsx.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;About Page&lt;/strong&gt;&lt;br&gt;
app/about/page.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";

const About = () =&amp;gt; {
  return (
    &amp;lt;div className='mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0'&amp;gt;
      &amp;lt;p&amp;gt;
        Lorem ipsum dolor sit amet consectetur adipisicing elit. Ex quos laborum
        aut voluptates qui vitae incidunt iusto ipsam, nam molestiae
        reprehenderit quisquam cum molestias ut nesciunt? Culpa incidunt nobis
        libero?
      &amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;Voluptate natus maiores, alias sapiente nisi possimus?&amp;lt;/p&amp;gt;
      &amp;lt;p&amp;gt;
        Ex amet eu labore nisi irure sit magna. Culpa minim dolor consequat
        dolore pariatur deserunt aliquip nisi eu ex dolor pariatur enim. Lorem
        pariatur cillum ullamco minim nulla ex voluptate. Occaecat esse mollit
        ipsum magna consectetur nulla occaecat non sit sint amet. Pariatur quis
        duis ut laboris ipsum velit fugiat do commodo consectetur adipisicing ut
        reprehenderit.
      &amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default About;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Blog Page&lt;/strong&gt;&lt;br&gt;
app/blog/page.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import { allPosts } from "contentlayer/generated";
import { compareDesc } from "date-fns";
import PostCard from "@/components/PostCard";

import "../../app/globals.css";

const Blog = () =&amp;gt; {
  const posts = allPosts.sort((a, b) =&amp;gt;
    compareDesc(new Date(a.date), new Date(b.date))
  );
  return (
    &amp;lt;div className='mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0'&amp;gt;
      &amp;lt;div className='space-y-2 pt-6 pb-8 md:space-y-5'&amp;gt;
        &amp;lt;h1 className='text-3xl mb-8'&amp;gt;Developer Blog&amp;lt;/h1&amp;gt;

        {posts.map((post, idx) =&amp;gt; (
          &amp;lt;div key={idx}&amp;gt;
            &amp;lt;hr className='grey-200 h-1 mb-10'&amp;gt;&amp;lt;/hr&amp;gt;
            &amp;lt;PostCard key={idx} {...post} /&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Blog;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Projects Page&lt;/strong&gt;&lt;br&gt;
This page contains three components.&lt;br&gt;
components/Fork.tsx which draws a fork.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/Fork.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/Fork.tsx&lt;/a&gt;&lt;br&gt;
components/Star.tsx which draws a star.&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/Star.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/Star.tsx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;components/Card.tsx which displays the github project data display in a card. &lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/Card.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/Card.tsx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Card.tsx uses types created in the file types.d.ts in the root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export type PrimaryLanguage = {
  color: string;
  id: string;
  name: string;
};

export type Repository = {
  description: string;
  forkCount: number;
  id?: number;
  name: string;
  primaryLanguage: PrimaryLanguage;
  stargazerCount: number;
  url: string;
};

type DataProps = {
  viewer: {
    login: string;
    repositories: {
      first: number;
      privacy: string;
      orderBy: { field: string; direction: string };
      nodes: {
        [x: string]: any;
        id: string;
        name: string;
        description: string;
        url: string;
        primaryLanguage: PrimaryLanguage;
        forkCount: number;
        stargazerCount: number;
      };
    };
  };
};

export type ProjectsProps = {
  data: Repository[];
};

export type SvgProps = {
  width: string;
  height: string;
  href?: string;
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can provide a link to your github projects instead of accessing them this way but I wanted to display them on my website instead of making the users leave. &lt;/p&gt;

&lt;p&gt;You will need to generate a personal access token from github. The github token is included in the .env.local file in this format:&lt;br&gt;
  GITHUB_TOKEN="Your token"&lt;/p&gt;

&lt;p&gt;Go to your Github and your profile. Choose Settings. It’s near the bottom of the menu. &lt;/p&gt;

&lt;p&gt;Go to Developer Settings. It’s at the bottom of the menu. Go to Personal access tokens.&lt;/p&gt;

&lt;p&gt;Choose generate new token ( classic ). You’ll see a menu with various permissions you can check. Everything is unchecked by default. At a minimum, you will want to check “public_repo”, which is under “repo”, and you’ll also want to check “read:user”, which is under “user.” Then click “Generate token”. Save that token (somewhere safe make sure it doesn’t make its way into your repository), and put it in your .env.local file. Now the projects should be able to be read with that token.&lt;/p&gt;

&lt;p&gt;More information: &lt;a href="https://docs.github.com/en/enterprise-server@3.6/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token"&gt;https://docs.github.com/en/enterprise-server@3.6/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;app/projects/page.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import { GraphQLClient, gql } from "graphql-request";
import Card from "@/components/Card";
import type { DataProps, Repository } from "@/types";

/**
 *
 * @param
 * @returns displays the list of user's github projects and descriptions
 */

export default async function Projects() {
  const endpoint = "https://api.github.com/graphql";

  if (!process.env.GITHUB_TOKEN) {
    return (
      &amp;lt;div className='mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0'&amp;gt;
        &amp;lt;div className='mx-auto divide-y'&amp;gt;
          &amp;lt;div className='space-y-2 pt-6 pb-8 md:space-y-5'&amp;gt;
            &amp;lt;h1 className='text-left text-3xl font-bold leading-9 tracking-tight sm:leading-10 md:text-3xl md:leading-14'&amp;gt;
              Projects
            &amp;lt;/h1&amp;gt;
            &amp;lt;p className='text-lg text-left leading-7'&amp;gt;
              Invalid Github token. Unable to access Github projects.
            &amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
  const graphQLClient = new GraphQLClient(endpoint, {
    headers: {
      authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
    },
  });

  const query = gql`
    {
      viewer {
        login
        repositories(
          first: 20
          privacy: PUBLIC
          orderBy: { field: CREATED_AT, direction: DESC }
        ) {
          nodes {
            id
            name
            description
            url
            primaryLanguage {
              color
              id
              name
            }
            forkCount
            stargazerCount
          }
        }
      }
    }
  `;

  const {
    viewer: {
      repositories: { nodes: data },
    },
  } = await graphQLClient.request&amp;lt;DataProps&amp;gt;(query);

  return (
    &amp;lt;&amp;gt;
      &amp;lt;div className='mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0'&amp;gt;
        &amp;lt;div className='mx-auto divide-y'&amp;gt;
          &amp;lt;div className='space-y-2 pt-6 pb-8 md:space-y-5'&amp;gt;
            &amp;lt;h1 className='text-left text-3xl font-bold leading-9 tracking-tight sm:leading-10 md:text-3xl md:leading-14'&amp;gt;
              Projects
            &amp;lt;/h1&amp;gt;
            &amp;lt;p className='text-lg text-left leading-7'&amp;gt;
              List of GitHub projects
            &amp;lt;/p&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div className='container py-4 mx-auto'&amp;gt;
            &amp;lt;div className='flex flex-wrap md:flex-wrap:nowrap'&amp;gt;
              {data.map(
                ({
                  id,
                  url,
                  name,
                  description,
                  primaryLanguage,
                  stargazerCount,
                  forkCount,
                }: Repository) =&amp;gt; (
                  &amp;lt;Card
                    key={id}
                    url={url}
                    name={name}
                    description={description}
                    primaryLanguage={primaryLanguage}
                    stargazerCount={stargazerCount}
                    forkCount={forkCount}
                  /&amp;gt;
                )
              )}
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/&amp;gt;
  );
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code checks for the github environment variable. If this variable is correct, it then creates a GraphQLClient to access the github api. The graphql query is set up to return the first 20 repositories by id, name, description, url, primary language, forks, and stars. You can adjust this to your needs by changing the query. The results are displayed in a Card component.&lt;/p&gt;

&lt;p&gt;Since we have not yet created a navigation menu type localhost://about, localhost://blog, localhost://projects to see your pages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Header, Navigation Bar, and Theme Changer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make a directory called _data at the top level. Add the file headerNavLinks.ts to this directory. This file contains names of your directories.&lt;/p&gt;

&lt;p&gt;_data/headerNavLinks.ts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const headerNavLinks = [
{ href: "/blog", title: "Blog" },
{ href: "/projects", title: "Projects" },
{ href: "/about", title: "About" },
];


export default headerNavLinks;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add:&lt;br&gt;
components/Header.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import Navbar from "./Navbar";

const Header = () =&amp;gt; {
  // useEffect only runs on the client, so now we can safely show the UI
  const [hasMounted, setHasMounted] = useState(false);

  // When mounted on client, now we can show the UI
  // Avoiding hydration mismatch
  // https://www.npmjs.com/package/next-themes#avoid-hydration-mismatch
  useEffect(() =&amp;gt; {
    setHasMounted(true);
  }, []);
  if (!hasMounted) {
    return null;
  }

  return (
    &amp;lt;div className='mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0 pt-0'&amp;gt;
      &amp;lt;header className='flex items-center justify-between py-10'&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;Link href='#'&amp;gt;
            &amp;lt;div className='flex lg:px-0'&amp;gt;
              {/* logo */}
              &amp;lt;Link href='/'&amp;gt;
                &amp;lt;div
                  id='logo'
                  className='flex-shrink-0 flex items-center bg-primary  h-16 w-25 border-radius'
                &amp;gt;
                  &amp;lt;span
                    id='logo-text'
                    className='text-blue-800  dark:text-blue-400 font-weight:bold text-3xl'
                  &amp;gt;
                    Logo
                  &amp;lt;/span&amp;gt;
                &amp;lt;/div&amp;gt;
              &amp;lt;/Link&amp;gt;
            &amp;lt;/div&amp;gt;
          &amp;lt;/Link&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;Navbar /&amp;gt;
      &amp;lt;/header&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Header;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is the navigation bar.&lt;/p&gt;

&lt;p&gt;app/components/NavBar.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import React, { useState } from "react";
import Link from "next/link";
import ThemeChanger from "./ThemeChanger";
import Hamburger from "./Hamburger";
import LetterX from "./LetterX";
import headerNavLinks from "@/data/headerNavLinks";
// names of header links are in
// separate file which allow them to be changed without affecting this component
/**
 *
 * @returns jsx to display the navigation bar
 */
const Navbar = () =&amp;gt; {
  const [navShow, setNavShow] = useState(false);

  const onToggleNav = () =&amp;gt; {
    setNavShow((status) =&amp;gt; {
      if (status) {
        document.body.style.overflow = "auto";
      } else {
        // Prevent scrolling
        document.body.style.overflow = "hidden";
      }
      return !status;
    });
  };

  return (
    &amp;lt;div className='flex items-center text-base leading-5 '&amp;gt;
      {/* show horizontal nav link medium or greater width */}
      &amp;lt;div className='hidden md:block'&amp;gt;
        {headerNavLinks.map((link) =&amp;gt; (
          &amp;lt;Link
            key={link.title}
            href={link.href}
            className='p-1 font-medium sm:p-4 transition duration-150 ease-in-out'
          &amp;gt;
            {link.title}
          &amp;lt;/Link&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;

      &amp;lt;div className='md:hidden'&amp;gt;
        &amp;lt;button
          type='button'
          className='ml-1 mr-1 h-8 w-8 rounded py-1'
          aria-controls='mobile-menu'
          aria-expanded='false'
          onClick={onToggleNav}
        &amp;gt;
          &amp;lt;Hamburger /&amp;gt;
        &amp;lt;/button&amp;gt;
        {/* when mobile menu is open move this div to x = 0
              when mobile menu is closed, move the element to the right by its own width,
              effectively pushing it out of the viewport horizontally.*/}

        &amp;lt;div
          className={`fixed top-0 left-0 z-10 h-full w-full transform bg-gray-100 opacity-95 duration-300 ease-in-out dark:bg-black ${
            navShow ? "translate-x-0" : "translate-x-full"
          }`}
        &amp;gt;
          &amp;lt;div className='flex justify-end'&amp;gt;
            &amp;lt;button
              type='button'
              className='mr-5 mt-14 h-8 w-8 rounded'
              aria-label='Toggle Menu'
              onClick={onToggleNav}
            &amp;gt;
              {/* X */}
              &amp;lt;LetterX /&amp;gt;
            &amp;lt;/button&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;nav className='fixed mt-8 h-full'&amp;gt;
            {headerNavLinks.map((link) =&amp;gt; (
              &amp;lt;div key={link.title} className='px-12 py-4'&amp;gt;
                &amp;lt;Link
                  href={link.href}
                  className='text-2xl  tracking-widest text-grey-900 dark:text-grey-100'
                  onClick={onToggleNav}
                &amp;gt;
                  {link.title}
                &amp;lt;/Link&amp;gt;
              &amp;lt;/div&amp;gt;
            ))}
          &amp;lt;/nav&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;ThemeChanger /&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default Navbar;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now for the theme change code.&lt;br&gt;
app/components/ThemeChanger.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import React, { useEffect, useState } from "react";
import { useTheme } from "next-themes";
import Moon from "./Moon";
import Sun from "./Sun";

/**
 *
 * @returns jsx to switch based on user touching the moon icon
 */
const ThemeChanger = () =&amp;gt; {
  const { theme, setTheme } = useTheme();
  const [mounted, setMounted] = useState(false);

  useEffect(() =&amp;gt; setMounted(true), []);

  if (!mounted) return null;

  return (
    &amp;lt;div&amp;gt;
      {theme === "light" ? (
        &amp;lt;button
          className='ml-1 mr-1 h-8 w-8 rounded p-1 sm:ml-4  text-gray-900 hover:text-gray-400'
          aria-label='Toggle light and dark mode'
          type='button'
          onClick={() =&amp;gt; setTheme("dark")}
        &amp;gt;
          &amp;lt;Moon /&amp;gt;
        &amp;lt;/button&amp;gt;
      ) : (
        &amp;lt;button
          className='ml-1 mr-1 h-8 w-8 rounded p-1 sm:ml-4  text-gray-50 hover:text-gray-400'
          aria-label='Toggle light and dark mode'
          onClick={() =&amp;gt; setTheme("light")}
        &amp;gt;
          &amp;lt;Sun /&amp;gt;
        &amp;lt;/button&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  );
};

export default ThemeChanger;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Links to the svg components Hamburger,  LetterX, Moon, Sun, LetterX :&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/Hamburger.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/Hamburger.tsx&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/LetterX.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/LetterX.tsx&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/Moon.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/Moon.tsx&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/Sun.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/Sun.tsx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now set up the theme provider which calls next themes.&lt;br&gt;
app/components/Theme-provider.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { ThemeProviderProps } from "next-themes/dist/types";

// https://github.com/pacocoursey/next-themes/issues/152#issuecomment-1364280564

export function ThemeProvider(props: ThemeProviderProps) {
  return &amp;lt;NextThemesProvider {...props} /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;app/providers.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";

import React from "react";
import { ThemeProvider as NextThemesProvider } from "next-themes";
import { ThemeProviderProps } from "next-themes/dist/types";

// https://github.com/pacocoursey/next-themes/issues/152#issuecomment-1364280564
// needs to be called NextThemesProvider not ThemesProvider
// not sure why
export function Providers(props: ThemeProviderProps) {
  return &amp;lt;NextThemesProvider {...props} /&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify app/layout.tsx to call the theme provider.&lt;/p&gt;

&lt;p&gt;Add these two lines to the top:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Header from "@/components/Header";
import { Providers } from "./providers";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrap the calls to the providers around the call to children.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Providers attribute='class' defaultTheme='system' enableSystem&amp;gt;
&amp;lt;Header /&amp;gt;
{children}
&amp;lt;/Providers&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In tailwind.config.ts, after plugins[], add:&lt;br&gt;
&lt;code&gt;darkMode: "class",&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Run npm dev. You should have everything working except the footer.&lt;br&gt;
For the footer, you can use the social icons here:&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/social-icons/Mail.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/social-icons/Mail.tsx&lt;/a&gt;&lt;br&gt;
I created a social-icons directory under app/components for the icons.&lt;br&gt;
The footer is a component:&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog/blob/main/components/Footer.tsx"&gt;https://github.com/donnabrown77/developer-blog/blob/main/components/Footer.tsx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Footer uses a file called siteMetaData.js that you  customize for your site.&lt;br&gt;
_data/siteMetData.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const siteMetadata = {
url: "https://yourwebsite.com",
title: "Next.js Coding Starter Blog",
author: "Your name here",
headerTitle: "Developer Blog",
description: "A blog created with Next.js and Tailwind.css",
language: "en-us",
email: "youremail@email.com",
github: "your github link",
linkedin: "your linkedin",
locale: "en-US",
};


module.exports = siteMetadata;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now add  in app/layout.tsx, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&amp;lt;Header /&amp;gt;
{children}
&amp;lt;Footer /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SEO&lt;br&gt;
Next JS 13 comes with SEO features.&lt;/p&gt;

&lt;p&gt;In app/layout.tsx, you can modify the defaults such as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const metadata: Metadata = {
title: "Home",
description: "A developer blog using Next JS 13",
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the blog pages, add this to app/posts/[slug]/page.tsx. This uses dynamic information, such as the current route parameters to return a metadata object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const generateMetadata = ({ params }: any) =&amp;gt; {
const post = allPosts.find(
(post: any) =&amp;gt; post._raw.flattenedPath === params.slug
);
return { title: post?.title, excerpt: post?.excerpt };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Link to github project:&lt;br&gt;
&lt;a href="https://github.com/donnabrown77/developer-blog"&gt;https://github.com/donnabrown77/developer-blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of the resources I used:&lt;br&gt;
&lt;a href="https://nextjs.org/docs/app/building-your-application/routing/colocation"&gt;https://nextjs.org/docs/app/building-your-application/routing/colocation&lt;/a&gt;&lt;br&gt;
&lt;a href="https://darrenwhite.dev/blog/nextjs-tailwindcss-theming"&gt;https://darrenwhite.dev/blog/nextjs-tailwindcss-theming&lt;/a&gt;&lt;br&gt;
&lt;a href="https://nextjs.org/blog/next-13-2#built-in-seo-support-with-new-metadata-api"&gt;https://nextjs.org/blog/next-13-2#built-in-seo-support-with-new-metadata-api&lt;/a&gt;&lt;br&gt;
&lt;a href="https://darrenwhite.dev/blog/dark-mode-nextjs-next-themes-tailwind-css"&gt;https://darrenwhite.dev/blog/dark-mode-nextjs-next-themes-tailwind-css&lt;/a&gt;&lt;br&gt;
&lt;a href="https://claritydev.net/blog/copy-to-clipboard-button-nextjs-mdx-rehype"&gt;https://claritydev.net/blog/copy-to-clipboard-button-nextjs-mdx-rehype&lt;/a&gt;&lt;br&gt;
&lt;a href="https://blog.openreplay.com/build-a-mdx-powered-blog-with-contentlayer-and-next/"&gt;https://blog.openreplay.com/build-a-mdx-powered-blog-with-contentlayer-and-next/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.sandromaglione.com/techblog/contentlayer-blog-template-with-nextjs"&gt;https://www.sandromaglione.com/techblog/contentlayer-blog-template-with-nextjs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://jpreagan.com/blog/give-your-blog-superpowers-with-mdx-in-a-next-js-project"&gt;https://jpreagan.com/blog/give-your-blog-superpowers-with-mdx-in-a-next-js-project&lt;/a&gt;&lt;br&gt;
&lt;a href="https://jpreagan.com/blog/fetch-data-from-the-github-graphql-api-in-next-js"&gt;https://jpreagan.com/blog/fetch-data-from-the-github-graphql-api-in-next-js&lt;/a&gt;&lt;br&gt;
&lt;a href="https://dev.to/arshadyaseen/build-a-blog-app-with-new-nextjs-13-app-folder-and-contentlayer-2d6h"&gt;https://dev.to/arshadyaseen/build-a-blog-app-with-new-nextjs-13-app-folder-and-contentlayer-2d6h&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>developers</category>
    </item>
    <item>
      <title>How to Build a Developer Blog using Next JS 13 and Contentlayer - Part two</title>
      <dc:creator>Donna Brown</dc:creator>
      <pubDate>Wed, 06 Sep 2023 22:35:47 +0000</pubDate>
      <link>https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-two-bp4</link>
      <guid>https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-two-bp4</guid>
      <description>&lt;p&gt;&lt;strong&gt;Next JS 13 structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The blog directory structure will follow a similar approach to the link listed below but I named the directory posts and placed it under the app directory. &lt;br&gt;
&lt;a href="https://nextjs.org/docs/getting-started/project-structure" rel="noopener noreferrer"&gt;https://nextjs.org/docs/getting-started/project-structure&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Create the file app/posts/page.tsx. Add this code to the file.&lt;/p&gt;

&lt;p&gt;app/posts/page.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Link from "next/link";
import { allPosts, Post } from "contentlayer/generated";
import { compareDesc } from "date-fns";

function PostCard(post: Post) {
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;h2&amp;gt;
        &amp;lt;Link href={post.url}&amp;gt;{post.title}&amp;lt;/Link&amp;gt;
      &amp;lt;/h2&amp;gt;
      &amp;lt;p&amp;gt;{post.excerpt}&amp;lt;/p&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

function page() {
  const posts = allPosts.sort((a, b) =&amp;gt;
    compareDesc(new Date(a.date), new Date(b.date))
  );

  return (
    &amp;lt;div&amp;gt;
      &amp;lt;div&amp;gt;
        {posts.map((post, idx) =&amp;gt; (
          &amp;lt;PostCard key={idx} {...post} /&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

export default page;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code sorts the generated posts by date. It then displays the link to the post, the title, and the excerpt.&lt;/p&gt;

&lt;p&gt;Create the directory app/posts/[slug]. Then create the file  app/posts/[slug]/page.tsx.  The square brackets mean this is a dynamic segment. When you don’t know the exact name  of  the file ahead of time and want to create routes from dynamic data. Note the function generateMetaData. Dynamic information, such as the current route parameters, can be set by exporting a generateMeta function that returns a Metadata object.&lt;/p&gt;

&lt;p&gt;app/posts/[slug]/page.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { DetailedHTMLProps, HTMLAttributes } from "react";
import { allPosts } from "contentlayer/generated";
import { useMDXComponent } from "next-contentlayer/hooks";
import type { MDXComponents } from "mdx/types";
import { format, parseISO } from "date-fns";
import { notFound } from "next/navigation";
import { CopyButton } from "@/components/CopyButton";
import "../../globals.css";

export const generateStaticParams = async () =&amp;gt;
  allPosts.map((post: any) =&amp;gt; ({ slug: post._raw.flattenedPath }));

export const generateMetadata = ({ params }: any) =&amp;gt; {
  const post = allPosts.find(
    (post: any) =&amp;gt; post._raw.flattenedPath === params.slug
  );
  return { title: post?.title, excerpt: post?.excerpt };
};

const PostLayout = ({ params }: { params: { slug: string } }) =&amp;gt; {
  const post = allPosts.find((post) =&amp;gt; post._raw.flattenedPath === params.slug);

  // 404 if the post does not exist.
  if (!post) notFound();

  const MDXContent = useMDXComponent(post!.body.code);

  const mdxComponents: MDXComponents = {
    // Override the default &amp;lt;pre&amp;gt; element
    pre: function ({
      children,
      ...props
    }: DetailedHTMLProps&amp;lt;HTMLAttributes&amp;lt;HTMLPreElement&amp;gt;, HTMLPreElement&amp;gt;) {
      const propsObj = { ...props };
      const propsValues = Object.values(propsObj);
      const [, , dataLanguage, dataTheme, code] = propsValues;
      const lang = dataLanguage || "shell";

      return (
        &amp;lt;pre data-language={lang} data-theme={dataTheme} className={"py-4"}&amp;gt;
          &amp;lt;div className='bg-gray-50 rounded-md overflow-x-auto'&amp;gt;
            &amp;lt;div
              className={
                "bg-gray-200 dark:text-black flex items-center relative px-4 py-2 text-sm font-sans justify-between rounded-t-md"
              }
            &amp;gt;
              {lang}
              &amp;lt;CopyButton text={code} /&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div className={"p-2"}&amp;gt;{children}&amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/pre&amp;gt;
      );
    },
  };

  return (
    &amp;lt;div className='mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0'&amp;gt;
      &amp;lt;h1 className='text-xl'&amp;gt;{post.title}&amp;lt;/h1&amp;gt;
      &amp;lt;p&amp;gt;
        &amp;lt;span className='text-gray-500 dark:text-gray-400'&amp;gt;
          {format(parseISO(post.date), "LLLL d, yyyy")}
        &amp;lt;/span&amp;gt;
      &amp;lt;/p&amp;gt;
      &amp;lt;article&amp;gt;
        &amp;lt;MDXContent components={mdxComponents} /&amp;gt;
      &amp;lt;/article&amp;gt;
    &amp;lt;/div&amp;gt;
  );
};

export default PostLayout;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the file app/components/CopyButton.tsx. CopyButton implements copy to clipboard functionality.&lt;/p&gt;

&lt;p&gt;app/components/CopyButton.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client"; // The "use client" directive is a convention to declare a boundary
// between a Server and Client Component module graph.

import { useState } from "react";
import ClipBoard from "./ClipBoard";

type Text = {
  text: string;
};
/**
 * CopyButton implements copy to clipboard functionality
 * @param text
 * @returns
 */
export const CopyButton = ({ text }: Text) =&amp;gt; {
  const [isCopied, setIsCopied] = useState(false);

  const copy = async () =&amp;gt; {
    await navigator.clipboard.writeText(text);
    setIsCopied(true);

    setTimeout(() =&amp;gt; {
      setIsCopied(false);
    }, 10000);
  };

  return (
    &amp;lt;button
      className='dark:text-black flex ml-auto gap-2'
      disabled={isCopied}
      onClick={copy}
    &amp;gt;
      {/* clipboard icon */}
      &amp;lt;ClipBoard /&amp;gt;
      {isCopied ? "Copied!" : "Copy code"}
    &amp;lt;/button&amp;gt;
  );
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the file app/components/Clipboard.tsx for the clipboard icon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";

const ClipBoard = () =&amp;gt; {
  return (
    &amp;lt;svg
      stroke='#000'
      fill='none'
      strokeWidth='2'
      viewBox='0 0 24 24'
      strokeLinecap='round'
      strokeLinejoin='round'
      className='h-4 w-4'
      height='1em'
      width='1em'
      xmlns='http://www.w3.org/2000/svg'
    &amp;gt;
      &amp;lt;path d='M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'&amp;gt;&amp;lt;/path&amp;gt;
      &amp;lt;rect x='8' y='2' width='8' height='4' rx='1' ry='1'&amp;gt;&amp;lt;/rect&amp;gt;
    &amp;lt;/svg&amp;gt;
  );
};

export default ClipBoard;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify contentlayer.config.ts:&lt;/p&gt;

&lt;p&gt;Add these lines after the first line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import rehypePrettyCode from "rehype-pretty-code";
import { visit } from "unist-util-visit";


/** @type {import('rehype-pretty-code').Options} */
const options: import("rehype-pretty-code").Options = {
  theme: {
   light: "light-plus",
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside mdx{}, add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rehypePlugins: [
      () =&amp;gt; (tree) =&amp;gt; {
        visit(tree, (node) =&amp;gt; {
          if (node?.type === "element" &amp;amp;&amp;amp; node?.tagName === "pre") {
            const [codeEl] = node.children;
            if (codeEl.tagName !== "code") return;
            node.raw = codeEl.children?.[0].value;
          }
        });
      },
      [rehypePrettyCode, options],
      () =&amp;gt; (tree) =&amp;gt; {
        visit(tree, (node) =&amp;gt; {
          // Select all div elements that contain a data-rehype-pretty-code-fragment data attribute.
          if (node?.type === "element" &amp;amp;&amp;amp; node?.tagName === "div") {
            if (!("data-rehype-pretty-code-fragment" in node.properties)) {
              return;
            }
            // Iterate over the pre children within each div (one for each theme) and
            // add the raw code content as a property to them.
            for (const child of node.children) {
              if (child.tagName === "pre") {
                child.properties["raw"] = node.raw;
              }
            }
          }
        });
      },
    ],

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function traverses the node tree of the content and extracts the unmodified ( raw text ) from all code elements nested inside the pre tag. It then stores the text content on the pre node.  This will give us a way to keep the unmodified code content  from the node’s raw property.&lt;/p&gt;

&lt;p&gt;Delete all the lines after the first three lines in globals.css. Add these lines after the tailwind setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
pre &amp;gt; code {
  display: grid;
}

code {
  counter-reset: line;
}

/* Apply line numbers only when showLineNumbers is specified: */
code[data-line-numbers] &amp;gt; [data-line]::before {
  counter-increment: line;
  content: counter(line);

  /* Other styling */
  display: inline-block;
  width: 1rem;
  margin-right: 2rem;
  text-align: right;
  color: gray;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you select a post containing a code block you should see syntax highlighting. Before the code block you should see  the name of the programming language and a copy button. If you click the copy button, the text will be pasted in an editor. You should see something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe282mr9gske9ykalh92d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe282mr9gske9ykalh92d.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In part three, I will show how to add a navbar, how to switch between dark and light mode, how to display stats on your github projects, and add information for seo. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Three&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-three-1155"&gt;https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-three-1155&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>developers</category>
    </item>
    <item>
      <title>How to Build a Developer Blog using Next JS 13 and Contentlayer - Part one</title>
      <dc:creator>Donna Brown</dc:creator>
      <pubDate>Wed, 06 Sep 2023 22:35:30 +0000</pubDate>
      <link>https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-one-k2c</link>
      <guid>https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-one-k2c</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This tutorial will show how to create a customizable blog for developers to write about technical topics and provide coding solutions. This blog will have these features:&lt;/p&gt;

&lt;p&gt;Code highlighting and formatting with a copy button&lt;br&gt;
Dark and light modes&lt;br&gt;
Responsive&lt;br&gt;
Display of github projects&lt;br&gt;
Add tags and categories to posts&lt;br&gt;
Search for specific topics in blog&lt;br&gt;
Sitemap for SEO&lt;/p&gt;

&lt;p&gt;Github repository:  &lt;a href="https://github.com/donnabrown77/developer-blog" rel="noopener noreferrer"&gt;https://github.com/donnabrown77/developer-blog&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;br&gt;
JavaScript, React, Vscode. Knowledge of TypeScript, Next JS, Tailwind CSS is helpful.&lt;/p&gt;

&lt;p&gt;Installation&lt;/p&gt;

&lt;p&gt;npx create-next-app@latest --ts&lt;/p&gt;

&lt;p&gt;Install the following npm packages:&lt;/p&gt;

&lt;p&gt;npm install @next/mdx contentlayer date-fns encoding graphql  graphql-request next-contentlayer next-themes  rehype-pretty-code remark-gfm shiki shiki-themes unist-utils-visit&lt;/p&gt;

&lt;p&gt;Package descriptions:&lt;/p&gt;

&lt;p&gt;@next/mdx - Sources data from local files, allowing you to create pages with an .mdx extension, directly in your /pages or /app directory.&lt;/p&gt;

&lt;p&gt;contentlayer - A content SDK that validates and transforms your content into type-safe JSON data you can easily import into your application’s pages.&lt;/p&gt;

&lt;p&gt;date-fns - Toolset for manipulating JavaScript dates in a browser and Node.js.&lt;/p&gt;

&lt;p&gt;encoding - Is a simple wrapper around iconv-lite to convert strings from one encoding to another.&lt;/p&gt;

&lt;p&gt;graphql - JavaScript reference implementation for GraphQL, a query language for apis.&lt;/p&gt;

&lt;p&gt;graphql-request - Minimal GraphQL client supporting Node and browsers for scripts or simple apps&lt;/p&gt;

&lt;p&gt;next-contentlayer - Plugin to tightly integrate Contentlayer into a Next.js&lt;/p&gt;

&lt;p&gt;next-themes - An abstraction for themes in your Next.js app.&lt;/p&gt;

&lt;p&gt;rehype-pretty-code -  plugin powered by the Shiki syntax highlighter that provides beautiful code blocks for Markdown or MDX.&lt;/p&gt;

&lt;p&gt;remark-gfm - plugin to support GFM (autolink literals, footnotes, strikethrough, tables, tasklists)&lt;/p&gt;

&lt;p&gt;shiki - syntax highlighter&lt;/p&gt;

&lt;p&gt;shiki-themes - themes for shiki&lt;/p&gt;

&lt;p&gt;unist-utils-visit - utility to visit nodes in a tree&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create sample blog posts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The blog post is a text file using a .mdx extension. MDX allows you to use JSX in your markdown content. You can import components, such as interactive charts or alerts, and embed them within your content.   Each .mdx file uses frontmatter.  Frontmatter is placed at the top of the MDX file between sets of three hyphens (---). Each frontmatter field should be placed on its own line. &lt;/p&gt;

&lt;p&gt;Here is a definition of frontmatter: &lt;a href="https://kabartolo.github.io/chicago-docs-demo/docs/mdx-guide/writing/#frontmatter" rel="noopener noreferrer"&gt;https://kabartolo.github.io/chicago-docs-demo/docs/mdx-guide/writing/#frontmatter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make a new directory called _posts in your root directory. I used the underscore as the first character for the directory name to make it a private folder in Next JS. Private folders are not considered by Next JS’s routing system. Here is a link to a explanation of private folders: &lt;a href="https://nextjs.org/docs/app/building-your-application/routing/colocation#private-folders" rel="noopener noreferrer"&gt;https://nextjs.org/docs/app/building-your-application/routing/colocation#private-folders&lt;/a&gt;.&lt;br&gt;
This is where the mdx files are located.  I created five frontmatter categories: title, date, tags, and author. Tags describe the topic or topics  for a blog post. A tag is used for searching a specific blog topic. Here are links to sample mdx files:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/donnabrown77/developer-blog/tree/main/_posts" rel="noopener noreferrer"&gt;https://github.com/donnabrown77/developer-blog/tree/main/_posts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can change these to whatever you need.&lt;/p&gt;

&lt;p&gt;next.config.js, tsconfig.json, contentlayerconfig.ts&lt;/p&gt;

&lt;p&gt;Create a file called next.config.js in your root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/** @type {import('next').NextConfig} */
const nextConfig = {};


const { withContentlayer } = require("next-content layer");


module.exports = withContentlayer({
experimental: {
appDir: true,
// https://stackoverflow.com/questions/75571651/using-remark-and-rehype-plugins-with-nextjs-13/75571708#75571708
mdxRs: false,
},
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify tsconfig.json to look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"baseUrl": ".",
"paths": {
"@/*": ["./*"],
"@/components/*": ["components/*"],
"@/data/*": ["_data/*"],
"contentlayer/generated": ["./.contentlayer/generated"],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the file contentlayer.config.ts in the root directory. &lt;br&gt;
Contentlayer is a content SDK that validates and transforms your content into type-safe JSON data you can easily import into your application.&lt;/p&gt;

&lt;p&gt;Add this code to that file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { defineDocumentType, makeSource } from "contentlayer/source-files";

const Post = defineDocumentType(() =&amp;gt; ({
  name: "Post",
  filePathPattern: `**/*.mdx`,
  contentType: "mdx",
  fields: {
    title: {
      type: "string",
      description: "The title of the post",
      required: true,
    },
    date: {
      type: "date",
      description: "The date of the post",
      required: true,
    },
    tags: {
      type: "json",
      description: "post topics",
      required: true,
    },
    excerpt: {
      type: "string",
      description: "summary of post",
      required: true,
    },
    author: {
      type: "string",
      description: "who wrote the post",
      required: false,
    },
  },
  computedFields: {
    url: {
      type: "string",
      resolve: (doc) =&amp;gt; `/posts/${doc._raw.flattenedPath}`,
    },
  },
}));

export default makeSource({
  contentDirPath: "_posts",
  documentTypes: [Post],
  mdx: {},
});

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a components directory.&lt;/p&gt;

&lt;p&gt;In the components directory, create two files MagnifyingGlass.tsx and Postcard.tsx. MagnifyingGlass.tsx is the svg to draw a maginifying  glass used for an input control. PostCard.tsx returns the jsx for a single post. &lt;/p&gt;

&lt;p&gt;app/components/MagnifyingGlass.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
/**
 *
 * @returns svg for search input control
 */
const MagnifyingGlass = () =&amp;gt; {
  return (
    &amp;lt;svg
      className='absolute right-3 top-3 h-5 w-5 text-gray-400 dark:text-gray-300'
      xmlns='http://www.w3.org/2000/svg'
      fill='none'
      viewBox='0 0 24 24'
      stroke='currentColor'
    &amp;gt;
      &amp;lt;path d='M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z'&amp;gt;&amp;lt;/path&amp;gt;
    &amp;lt;/svg&amp;gt;
  );
};

export default MagnifyingGlass;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;app/components/PostCard.tsx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from "react";
import { Post } from "contentlayer/generated";
import Link from "next/link";
import { format, parseISO } from "date-fns";

/**
 *
 * @param post
 * @returns jsx to display post
 */
const PostCard = (post: Post) =&amp;gt; {
  let tags = [...post.tags];
  return (
    &amp;lt;article className='mb-12'&amp;gt;
      &amp;lt;div className='space-y-2 xl:grid xl:grid-cols-4 xl:items-baseline xl:space-y-0'&amp;gt;
        &amp;lt;dl&amp;gt;
          &amp;lt;dd className='text-gray-500 dark:text-gray-400 text-base font-medium leading-6'&amp;gt;
            &amp;lt;time dateTime={post.date} className='mr-2 '&amp;gt;
              {format(parseISO(post.date), "LLLL d, yyyy")}
            &amp;lt;/time&amp;gt;
          &amp;lt;/dd&amp;gt;
        &amp;lt;/dl&amp;gt;
        &amp;lt;span className='text-gray-500 dark:text-gray-400'&amp;gt;{post.author}&amp;lt;/span&amp;gt;
        &amp;lt;div className='space-y-5 xl:col-span-3'&amp;gt;
          &amp;lt;div className='space-y-6'&amp;gt;
            &amp;lt;div&amp;gt;
              &amp;lt;h2 className='text-2xl font-bold leading-8 tracking-tight'&amp;gt;
                &amp;lt;Link href={post.url}&amp;gt;{post.title}&amp;lt;/Link&amp;gt;
              &amp;lt;/h2&amp;gt;
              &amp;lt;div className='flex flex-wrap'&amp;gt;
                {post.tags &amp;amp;&amp;amp; (
                  &amp;lt;ul className='inline-flex'&amp;gt;
                    {tags.map((tag) =&amp;gt; (
                      &amp;lt;li
                        key={post.date}
                        className='mr-3 uppercase block text-sm text-blue-800 dark:text-blue-400 font-medium'
                      &amp;gt;
                        {tag}
                      &amp;lt;/li&amp;gt;
                    ))}
                  &amp;lt;/ul&amp;gt;
                )}
              &amp;lt;/div&amp;gt;
            &amp;lt;/div&amp;gt;

            &amp;lt;div className='text-gray-500 dark:text-gray-400 prose max-w-none'&amp;gt;
              {post.excerpt}
            &amp;lt;/div&amp;gt;
          &amp;lt;/div&amp;gt;
          &amp;lt;div className='text-base font-medium leading-6'&amp;gt;
            &amp;lt;Link
              href={post.url}
              className=' text-blue-800 dark:text-blue-400 hover:text-blue-400 dark:hover:text-blue-200'
            &amp;gt;
              Read more →
            &amp;lt;/Link&amp;gt;
          &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/article&amp;gt;
  );
};

export default PostCard;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Changes to page.tsx in app directory.&lt;/p&gt;

&lt;p&gt;Remove everything from page.tsx and replace it with this code. Note the use of “use client”; at the top  of the file which makes it a client component instead of the default server component in Next JS 13. When a user types in characters in the input control, the text is compared to the tags field of the mdx file. If there are posts with any of those tags, the page is rerendered to show only posts containing those tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import React, { useState } from "react";
import { allPosts } from "contentlayer/generated";
import { compareDesc } from "date-fns";
import PostCard from "@/components/PostCard";
import MagnifyingGlass from "@/components/MagnifyingGlass";
import "./globals.css";

export default function Home() {
  // handleInput will contain an array of topics or nothing
  // if there is something returned, look for a matching post
  // then display matching posts
  const [topic, setTopic] = useState&amp;lt;string&amp;gt;("");
  // get text from input control, use the value to set the topic.
  const handleInput = (event: React.ChangeEvent&amp;lt;HTMLInputElement&amp;gt;) =&amp;gt; {
    setTopic(event.currentTarget.value);
  };

  // given an array of tags, return if there is a matching topic
  function findTags(t: string[]) {
    for (const element of t) {
      // Matching topics are not case sensitive
      let str1: string = element.toString().toLowerCase();
      let str2: string = topic.toString().toLowerCase();
      if (str1 === str2) return true;
    }
    return false;
  }
  let posts = allPosts.sort((a, b) =&amp;gt;
    compareDesc(new Date(a.date), new Date(b.date))
  );

  // test array of tags for matching topic
  let filteredPosts = posts.filter((post) =&amp;gt; findTags(post.tags));
  // if there are a posts which match the topic, display only those posts
  if (filteredPosts.length &amp;gt; 0) posts = filteredPosts;

  return (
    &amp;lt;div className='mx-auto max-w-3xl px-4 sm:px-6 xl:max-w-5xl xl:px-0'&amp;gt;
      &amp;lt;div className='space-y-2 pt-6 pb-8 md:space-y-5'&amp;gt;
        &amp;lt;h1 className='text-3xl mb-8'&amp;gt;Developer Blog&amp;lt;/h1&amp;gt;
        &amp;lt;div className='relative max-w-lg mb-5 dark:bg-transparent'&amp;gt;
          &amp;lt;input
            type='text'
            aria-label='Search articles'
            placeholder='Search articles'
            value={topic}
            onChange={handleInput}
            size={100}
            className='block w-full rounded-md border border-gray-300 bg-white px-4 py-2 text-gray-900 focus:border-primary-500 focus:ring-primary-500 dark:border-gray-900 dark:bg-gray-800 dark:text-gray-100'
          &amp;gt;&amp;lt;/input&amp;gt;
          &amp;lt;MagnifyingGlass /&amp;gt;
        &amp;lt;/div&amp;gt;
        {posts.map((post, idx) =&amp;gt; (
          &amp;lt;div key={idx}&amp;gt;
            &amp;lt;hr className='grey-200 h-1 mb-10'&amp;gt;&amp;lt;/hr&amp;gt;
            &amp;lt;PostCard key={idx} {...post} /&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try running npm run dev. Your screen should look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4n2qigzvzsn9l5e96ms.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv4n2qigzvzsn9l5e96ms.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part Two&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-two-bp4"&gt;https://dev.to/dbrownsoftware/how-to-build-a-developer-blog-using-next-js-13-and-contentlayer-part-two-bp4&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>react</category>
      <category>developers</category>
    </item>
    <item>
      <title>How to read a CSV file using React functional components</title>
      <dc:creator>Donna Brown</dc:creator>
      <pubDate>Mon, 16 May 2022 18:30:03 +0000</pubDate>
      <link>https://dev.to/dbrownsoftware/how-to-read-a-csv-file-using-react-functional-components-44m9</link>
      <guid>https://dev.to/dbrownsoftware/how-to-read-a-csv-file-using-react-functional-components-44m9</guid>
      <description>&lt;p&gt;Here is code to read in a .csv file using React hooks and functional components. CsvReader is a functional component which uses the useState hook to save the filename and data read in from the file to state variables. This is GitHub link: &lt;a href="https://github.com/donnabrown77/csv-reader"&gt;https://github.com/donnabrown77/csv-reader&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I included a child component to display the CSV data in  an HTML table. Here is a link to this code:&lt;br&gt;
&lt;a href="https://dev.to/dbrownsoftware/how-to-create-a-reusable-html-table-in-react-30pf"&gt;https://dev.to/dbrownsoftware/how-to-create-a-reusable-html-table-in-react-30pf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First create a functional component and state variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CsvReader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// save the input file name to a state variable&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;csvFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCsvFile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// save the file data to a state variable&lt;/span&gt;
  &lt;span class="c1"&gt;// so the DisplayTable component can use it&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;csvArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCsvArray&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each line of csv data is separated by a newline character. So that needs to be removed. Use the map function to replace commas with a space. Then call setCsvArray with the row data.&lt;/p&gt;

&lt;p&gt;Next, write a function to handle the file upload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleFileUpload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;csvFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;processCSV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readAsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Setup the functional component.  Return jsx with input type="file" and accept=".csv" and a button with an onClick event handler.  DisplayTable is a child component I wrote to display html table without hard coding the table header, row, and cell data.  It is optional.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;accept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.csv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;setCsvFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
        &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nx"&gt;handleFileUpload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;Submit&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DisplayTable&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;csvArray&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;CsvReader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the final result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;DisplayTable&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./DisplayTable&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CsvReader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// save the input file name to a state variable&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;csvFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCsvFile&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// save the file data to a state variable&lt;/span&gt;
  &lt;span class="c1"&gt;// so the DisplayTable component can use it&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;csvArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCsvArray&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processCSV&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// split the file data into rows from the newline character&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// remove comma&lt;/span&gt;
    &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/,/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;setCsvArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleFileUpload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;csvFile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;FileReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;processCSV&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nx"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;readAsText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;accept&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.csv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;setCsvFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;
        &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nx"&gt;handleFileUpload&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;Submit&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;br&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DisplayTable&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;csvArray&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;CsvReader&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>react</category>
      <category>tutorial</category>
      <category>csv</category>
    </item>
    <item>
      <title>How to create a reusable HTML table in React</title>
      <dc:creator>Donna Brown</dc:creator>
      <pubDate>Mon, 16 May 2022 18:26:34 +0000</pubDate>
      <link>https://dev.to/dbrownsoftware/how-to-create-a-reusable-html-table-in-react-30pf</link>
      <guid>https://dev.to/dbrownsoftware/how-to-create-a-reusable-html-table-in-react-30pf</guid>
      <description>&lt;p&gt;Here is code to display an HTML table in React. I assumed the first row to be a table header. This may not  work for some cases where there may more than one table header. I kept the .css file simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./DisplayTable.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableHeader&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tbody&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TableRow&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="p"&gt;))}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tbody&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/table&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TableRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;td&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/td&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;))}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TableHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;thead&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;th&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/th&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="p"&gt;))}&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/tr&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/thead&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DisplayTable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// convert each row of data to an array of rows&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// take first row off to get table data&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// get first row off to make table headings&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstRow&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;firstRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// make an array of strings&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Table&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Table&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;DisplayTable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DisplayTable.css&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;td {
  text-align: right;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
  </channel>
</rss>
