title: Is there a generic way of building and maintaining a nested menu in React (Next.js)?
body: <p>The past couple of days, I've been working on a project with a navigation bar, implemented with TypeScript and React (Next.js). Until now, I've been using a single level navigation, but I'm moving towards a multi-level navigation menu, and I could use some help with it.</p>
  <h1>Initial code: Single Level Menu</h1>
  <p>The initial code I have written was for a Single Level Menu, i.e. a simple navigation bar without submenus, containing links on the top level only. The code is as follows:</p>
  <p><strong>1) Model / Type Definition</strong></p>
  <pre class="lang-js prettyprint-override"><code>// @/models/NavLinks.d.ts
  
  export interface NavItem {
    title: string;
    link: string;
    ariaLabel?: string;
  }
  
  export interface NavItemCollection {
    title?: string; // Titles are used in Sidebar, not in main Navbar in this example
    items: Array&lt;NavItem&gt;;
  }
  
  export type NavItemCollections = NavItemCollection[];
  </code></pre>
  <p><strong>2) Data:</strong></p>
  <pre class="lang-js prettyprint-override"><code>// @/data/MainNav.ts
  
  import { NavItemCollections } from &quot;@/models/NavLinks&quot;;
  
  export const NavLinks: NavItemCollections = [
    {
      items: [
        { title: &quot;About&quot;, link: &quot;/about&quot; },
        { title: &quot;Security&quot;, link: &quot;/security&quot; },
        { title: &quot;Blog&quot;, link: &quot;/blog&quot; },
      ],
    },
  ];
  </code></pre>
  <p><strong>3) Usage:</strong></p>
  <pre class="lang-js prettyprint-override"><code>// @/components/site/Navbar.tsx;
  
  import Link from &quot;next/link&quot;;
  import { NavItemCollections } from &quot;@/models/NavLinks&quot;;
  import styles from &quot;@/styles/Navbar.module.scss&quot;;
  
  export interface NavbarProps {
    links: NavItemCollections;
  }
  
  export const Navbar = (props: NavbarProps) =&gt; {
    return (
      &lt;&gt;
        &lt;nav&gt;
         {props.links.map((collection) =&gt;
            collection.items.map((item, index) =&gt; (
              &lt;Link href={item.link} key={index}&gt;
                &lt;a className={styles.link}&gt;{item.title}&lt;/a&gt;
              &lt;/Link&gt;
            ))
          )}
        &lt;/nav&gt;
      &lt;/&gt;
    );
  }
  </code></pre>
  <p>... and then passing the links using the prop as specified. I.e. <code>&lt;Navbar links={links} /&gt;</code> in a parent component.</p>
  <p>P.S. Keep in mind that Next.js requires me to wrap <code>&lt;a&gt;</code> inside <code>&lt;Link&gt;</code>.</p>
  <h1>My Goal: Multi Level Menu</h1>
  <p>My personal goal is to refactor the entire menu, so that I (and others) have one uniform way of implementing a well-structured, well-typed navigation menu that could be used in multiple places.</p>
  <p>In order to do so, I borrowed some code from VuePress 2, which uses something similar in their default theme.</p>
  <p><strong>1) Model / Type Definition</strong></p>
  <pre class="lang-js prettyprint-override"><code>// @/models/NavLinks.d.ts
  
  /**
   * Base nav item, displayed as text
   */
  export interface NavItem {
    text: string;
    ariaLabel?: string;
  }
  
  /**
   * Base nav group, has nav items children
   */
  export interface NavGroup&lt;T&gt; extends NavItem {
    children: T[];
  }
  
  /**
   * NavLink, i.e. inherited from NavItem, but extended with link properties.
   */
  export interface NavLink extends NavItem {
    link: string;
    rel?: string;
    target?: string;
  }
  
  /**
   * Navbar Contents
   */
  export type NavbarItem = NavLink;
  export type NavbarGroup = NavGroup&lt;NavbarGroup | NavbarItem | string&gt;;
  export type NavbarContents = (NavbarItem | NavbarGroup | string)[];
  </code></pre>
  <p><strong>2) Data</strong></p>
  <p>With our new model, we could work a multi-level menu, i.e. items with children, with items and optionally children, etc. In other words: a nested navigation structure. Cool.</p>
  <pre class="lang-js prettyprint-override"><code>// @/data/MainNav.ts
  
  import { NavbarContents } from &quot;@/models/NavLinks&quot;;
  
  export const NavLinks: NavbarContents = [
    { text: &quot;About&quot;, link: &quot;/about&quot; },
    { text: &quot;Security&quot;, link: &quot;/security&quot; },
    { text: &quot;Blog&quot;, link: &quot;/blog&quot; },
    {
      text: &quot;Professional&quot;,
      children: [
        { text: &quot;Individual&quot;, link: &quot;/professional&quot; },
        { text: &quot;Business&quot;, link: &quot;/business&quot; },
      ],
    },
  ];
  </code></pre>
  <p><strong>3) Usage:</strong></p>
  <p>This is where I got stuck. If I am using the same <code>.map()</code> approach as shown earlier, I get the following error:</p>
  <pre><code>Property 'map' does not exist on type 'string | NavLink | NavbarGroup'.
  Property 'map' does not exist on type 'string'.ts(2339)
  </code></pre>
  <p>The error is quite obvious, as you cannot use <code>.map</code> on a <code>string</code> type, but what should I use instead? I tried to use <code>typeof</code> in order to decide if it's a string. Then just render the string...</p>
  <pre><code>&lt;nav className={styles.navlinks}&gt;
           {props.links.map((collection) =&gt;
             typeof (collection) === &quot;string&quot; ?
               &lt;span className=&quot;string&quot;&gt;{collection}&lt;/span&gt;
               : (...)
  </code></pre>
  <p>And I'm left with either a <code>NavLink</code> or <code>NavbarGroup</code>. No clue how to implement them.</p>
  <p>The question is: How can I have a nested menu using the type definition and data from above?</p>