<p>I pulled together this brain-dump of 8 different ways of doing navigation. There are probably more. With any method make sure you keep a close eye on the query count and load speed, as some of these methods are very resource-intensive. You can mitigate with the <code>cache</code> tag, but ideally should strive for simplicity where possible. Don't be afraid of hardcoding things, it's a totally acceptable solution.</p>
<p>One &quot;mistake&quot; (in my opinion) that I see often in inherited sites is a navigation system that is a direct reflection of the actual content - usually this takes the form of a multi-level &quot;Pages&quot; Structure section that contains everything in the entire site!  Ultimately, your content model should be fully divorced from your actual navigation: for a more thorough monologue about why that's a bad idea in any CMS, not just Craft, see my article, <a href="https://cld.agency/blog/content-utopia" rel="nofollow noreferrer">Content Utopia</a>.</p>
<h1>nav approaches</h1>
<ol>
<li>Completely hardcoded<br>
Pros: Pure awesome<br>
Cons: Not translatable, no client access</li>
<li>A global table with plain text columns for URI and label (see example code below)
<br>Pros: Light (zero extra queries), flexibility to add any URI
<br>    Cons: No entry selector possible - need to find (and update) URIs manually. No child items (update: actually, you <em>can</em> add a column to mark items as 'level-2'... the template coding to gather them up and build nested lists under the previous parent item is a little complex, but it works well if you only need 2 levels).</li>
<li>Hardcoded but with static translations
<br>Pros: Fairly awesome
<br>Cons: A bit fiddly, and need to translate URIs manually. No client access.</li>
<li>A structure with fields for entry select and arbitrary URL override
<br>Pros: Can accommodate child items easily using craft's <code>nav</code> tag. Intuitive UI.
<br>Cons: Heavy query count, hard to administer all nav items in one go as they're separate entries. Nav management really belongs in Globals IMO, not Entries.</li>
<li>A global matrix with fields for entry select, label override, and arbitrary URL override
<br>Pros: Very flexible
<br>Cons: Heavy query count (8 with eager loading, 10 without). Matrix interface is a bit unwieldy just for a list of links. Can't handle child items without adding a table or supertable block.</li>
<li>A global entries selector
<br>Pros: Nice simple interface. Automatically updating URIs and labels
<br>Cons: Fairly heavy query count. No flexibility to override (unless you put extra fields against the entries). No child items (without adding fields to each entry). No arbitrary items (everything must be an entry).</li>
<li>Hybrid. Depending on the nature of the site, some parts of the nav could be pulled from section entries or category groups while others parts are hardcoded.
<br>Pros: Lots of client control. Changeable dynamic nav.
<br>Cons: Fairly heavy query count (use cache tag). Nav is not fully administered in one single place.</li>
<li>A plugin (ugh)
<br>Cons: it's an unnecessary plugin, just don't do it!</li>
</ol>
<p>Just in case anyone else likes the idea of #2 above (I think it's becoming my favourite approach personally), here's how you can use that and also cater for a second level of child items. Probably pushes Twig into overly-complex territory, but I'd still rather do this than install a plugin. This approach uses a global table field called <code>primaryNavigation</code> in a global set called <code>navigation</code> with 3 columns: <code>label</code> (text), <code>link</code> (text), <code>level2</code> (checkbox).</p>
<pre class="lang-twig prettyprint-override"><code>{# ======================================
FIRST, LET's MAKE A HASH MAP to match up 
parents with their children based on their
position in the list
========================================= #}

{% set primaryNav = navigation.primaryNavigation %}
{% set children = {} %}

{% for item in primaryNav %}
    {% set targetArray = {} %}

    {% if item.level2 %}
        {% if attribute(children, lastKnownTopLevelItem) is not defined %}
            {% set children = children|merge({(lastKnownTopLevelItem): [item]}) %}
        {% else %}
            {% set targetArray = attribute(children, lastKnownTopLevelItem) %}
            {% set targetArray = targetArray|merge([item]) %}
            {% set children = children|merge({(lastKnownTopLevelItem): targetArray}) %}
        {% endif %}
    {% endif %}

    {# make a note of the loop index of the last known top level item... #}
    {% if not item.level2 %}
        {% set lastKnownTopLevelItem = 'children-of-' ~ loop.index %}
    {% endif %}
{% endfor %}

{# ======================================
NOW OUTPUT...
========================================= #}

&lt;ul&gt;
    {# just loop through top level, as we will fetch child items
from the hash map... beware, don't combine the 'not level2'
conditional into the for loop as otherwise the loop indices won't
 match up! #}
    {% for item in primaryNav %}
        {% if not item.level2 %}

            {% set childrenOfThisItem = attribute(children,'children-of-' ~ loop.index) ?? {} %}

            {% if childrenOfThisItem|length %}
                &lt;li&gt;
                    &lt;a href=&quot;{{ item.link }}&quot;&gt;{{ item.label }}&lt;/a&gt;
                    &lt;ul&gt;
                        {% for child in childrenOfThisItem %}
                            &lt;li&gt;&lt;a href=&quot;{{ child.link }}&quot;&gt;{{ child.label }}&lt;/a&gt;&lt;/li&gt;
                        {% endfor %}
                    &lt;/ul&gt;
                &lt;/li&gt;
            {% else %}
                &lt;li&gt;&lt;a href=&quot;{{ item.link }}&quot;&gt;{{ item.label }}&lt;/a&gt;&lt;/li&gt;
            {% endif %}
        {% endif %}
    {% endfor %}
&lt;/ul&gt;
</code></pre>

CC BY-SA 4.0
2017-02-01T17:32:50.130Z
2024-05-08T11:11:20.630Z
2024-05-08T11:11:20.630Z
James Smith
<p>In some cases I use a pre-defined (very simple) nav that I don't need to be driven from the CMS yet I want to share it between the header / footer.  In these cases I'll set an array in my base layout template so I can use it throughout.</p>

<p>In "_partials/layout.twig" template:</p>

<pre><code>{% set nav = [
    {'title':'Home', 'link':siteUrl, 'class':'homepage'},
    {'title':'About', 'link':url('about'), 'class':'about-us'},
    {'title':'Contact', 'link':url('contact'), 'class':'contact-us'},
]%}
</code></pre>

<p>In "_partials/header.twig" template:</p>

<pre><code>&lt;nav&gt;
{# logo, etc... #}
{% if (nav is defined) and (nav is iterable) %}
    &lt;ul&gt;
    {% for navItem in nav %}
        &lt;li class="{{ navItem.class }}"&gt;&lt;a href="{{ navItem.link }}"&gt;{{ navItem.title }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% endif %}
&lt;nav&gt;
</code></pre>

<p>In "_partials/footer.twig" template:</p>

<pre><code>{% if (nav is defined) and (nav is iterable) %}
    &lt;ul&gt;
    {% for navItem in nav %}
        &lt;li class="{{ navItem.class }}"&gt;&lt;a href="{{ navItem.link }}"&gt;{{ navItem.title }}&lt;/a&gt;&lt;/li&gt;
    {% endfor %}
    &lt;/ul&gt;
{% endif %}
</code></pre>

<p>Very simple concept I know.  But at the end of the day when I know the nav isn't going to change drastically or I don't want the end user to alter it, this is what I do.  It has practically no impact on performance and allows me to manage the items in one place.</p>

CC BY-SA 3.0
2014-06-12T18:50:32.223Z
That looks very cool, since it is for a super simple Navi.
KSPR
CC BY-SA 3.0
2014-06-12T16:33:03.313Z
2014-06-12T16:33:03.313Z
nateiler
<p>If you have more complex menus in the future, you should take a look at the <a href="http://buildwithcraft.com/docs/templating/tags#nav">Nav-Tag</a>.</p>

<p>.</p>

<p><strong>Getting singles:</strong></p>

<p>There isn't an easy way to get singles (yet). But you can use the same method which is used in crafts backend:</p>

<pre><code>{% set allSections = craft.sections.getAllSections() %}
{% set singleSections = [] %}

{% for section in allSections %}
    {% if section.type == 'single' %}
        {% set singleSections = singleSections|merge([section]) %}
    {% endif %}
{% endfor %}

{% set navi = craft.entries.section(singleSections) %}
</code></pre>

<p>.</p>

<p><strong>Queries:</strong></p>

<p>In your case there isn't a difference. But if you plan on using the same query twice, you should save it into a var and reuse it:</p>

<p>This example only makes one db call:</p>

<pre><code>{% set navi = craft.entries.section('not projekteintrag' ).order('id asc') %}

{% for entry in navi %} ... {% endfor %}

{% for entry in navi %} ... {% endfor %}

{% for entry in navi %} ... {% endfor %}
</code></pre>

<p>But this makes three:</p>

<pre><code>{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %} ... {% endfor %}

{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %} ... {% endfor %}

{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %} ... {% endfor %}
</code></pre>

CC BY-SA 3.0
2014-06-12T15:46:01.383Z
2014-06-12T15:46:01.383Z
Victor
<p>1) This is not so simple of an answer. Craft itself uses JS to handle the content of indexes, so I'm not sure how they're doing this. Using what's available you would need to build a list of Single Sections, then pull entries based on that list.</p>

<pre><code>{% set sections = [] %}
{% for section in craft.sections.getAllSections() %}
    {% if section.type == 'single' %}
        {% set sections = sections|merge([section]) %}
    {% endif %}
{% endfor %}
</code></pre>

<p>Then your query would be (however you choose to use it):</p>

<pre><code>craft.entries.section(sections).order('id asc')
</code></pre>

<p>2) Both examples are the same. You can keep adding/editing criteria until you actually pull the entries by using an output function (find, first, etc) or using it in a loop (such as for and nav)</p>

CC BY-SA 3.0
2014-06-12T18:51:11.163Z
What does [] in the first variable do?
KSPR
CC BY-SA 3.0
2014-06-12T19:08:48.477Z
It's an empty array. Later we add our section to it my making it a single-item array and merging them together.
Bryan Redeagle
CC BY-SA 3.0
2014-06-12T15:46:18.947Z
2014-06-12T15:46:18.947Z
Bryan Redeagle
<p>Another easy way to handle a dynamic menu in Craft is to add a Matrix field to a Global.</p>

<p>For example, I have a Global called "Main Menu". That Global has a Matrix field called "Menu Item". I then have a block called "Item" that has two plain text fields. One field is for the "label" and one field is for the "URI" that I want users to be directed to.</p>

<p>Then in my template file I have the following.</p>

<pre><code>{% for block in mainMenu.menu %}
    {% if block.type == "item" %}
      &lt;li class="item"&gt;
        &lt;a href="{{ block.itemUri }}"&gt;{{ block.label }}&lt;/a&gt;
      &lt;/li&gt; 
    {% endif %}
{% endfor %}
</code></pre>

CC BY-SA 3.0
2017-02-01T16:53:57.587Z
If you're only using plain text fields, you can do this more efficiently with a table fieldtype instead of a matrix. The matrix approach would be if you want to use an entries selector to get the URIs (however, I've found that to be highly inefficient, adding an extra 10 queries per page load)
James Smith
CC BY-SA 3.0
2016-02-29T01:10:30.850Z
2016-02-29T01:10:30.850Z
Ethan Thompson
<p>This doesn't work for every project, but for brochure style sites I like having all the client-arrangeable pages in one Structure, with toggles on each entry to optionally show in primary or secondary navigation, and Entry Types for different requirements such as a team page or articles section, and reserving Singles for things like homepage, privacy policy etc. It makes things very intuitive for clients to get the hang of (even though it makes it easier for them to potentially break things).</p>

CC BY-SA 4.0
2019-10-17T13:09:52.637Z
2019-10-17T13:09:52.637Z
richardschaefer
<p>My solution is to setup a dedicated "Menu" section and use an entries field type for the links.  This allows you to pull together entries from multiple sections into a single menu.</p>

<p>My normal setup is to have to custom fields</p>

<ul>
<li>relatedEntry - Entries field allowing you to select any other entry</li>
<li>customURL - text field for custom url input</li>
</ul>

<p>The idea being that you could fill in either or and have a dynamic menu with links to entries or custom links.</p>

<p>Then on the template side you simple would do this</p>

<pre><code>{% set menu = craft.entries.section('menu') %}
&lt;ul&gt; 
{% nav link in menu %}
    &lt;li&gt;

        {% if link.relatedEntry|length %}
                &lt;a href="{{ link.relatedEntry[0].url }}"&gt;{{ link.title }}&lt;/a&gt;
        {% else %}
                &lt;a href="{{ link.customURL }}"&gt;{{ link.title }}&lt;/a&gt;
        {% endif %}

        {% ifchildren %}
            &lt;ul&gt;
                {% children %}
            &lt;/ul&gt;
        {% endifchildren %}
    &lt;/li&gt; 
{% endnav %}
&lt;/ul&gt;
</code></pre>

<p>I hope that helps</p>

CC BY-SA 3.0
2015-05-21T13:25:37.340Z
How would you add an active class to each link with this configuration?
KSPR
CC BY-SA 3.0
2015-05-22T11:40:20.763Z
For each link in the loop just add an if craft.request.url == this link
Keith Mancuso
CC BY-SA 3.0
2018-03-16T17:07:49.623Z
The main reason I don't like this approach (other than the performance hit), is that it requires the editor to manage the menu in the 'Entries' area, when intuitively it feels like navigation should be managed in the 'Globals' area.
James Smith
CC BY-SA 3.0
2014-06-12T17:14:25.570Z
This is the best answer in my opinion and if you want to ensure it's a single level you need only to set the structure section to a max level of 1 and remove the {% ifchildern %} tags from the above code. Complete flexibility for nav link order, where links go, etc.
Stuart McCoy
CC BY-SA 3.0
2014-06-13T10:03:26.133Z
I have also used this method on 4 sites now and it works well. I just wish we could add the fields to a tab in a field set instead of needing a separate section.
Bryan Garrant
CC BY-SA 3.0
2014-06-12T17:02:15.770Z
2014-06-12T17:02:15.770Z
Keith Mancuso
<p>After endless trial and error I figured out the right syntax for a simple one level navigation. </p>

<pre><code>{% set navi = craft.entries.section('not projekteintrag' ) %} 
{% for entry in navi.order('id asc') %}
  &lt;li&gt;&lt;a href="{{ entry.url }}"&gt;{{ entry.navigationTitle }}&lt;/a&gt;&lt;/li&gt;
{% endfor %}
</code></pre>

<ol>
<li>Is there a parameter to just trigger single pages? Right now I grabbed the singles with filtering the entries.</li>
<li>What is the difference between writing the upper syntax and:</li>
</ol>

<p>-</p>

<pre><code>{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %}
  &lt;li&gt;&lt;a href="{{ entry.url }}"&gt;{{ entry.navigationTitle }}&lt;/a&gt;&lt;/li&gt;
{% endfor %}
</code></pre>

<templating><navigation><singles>
How to write a simple navigation?
# How to write a simple navigation?

(Asked by: username_0 on 2014-06-12)

After endless trial and error I figured out the right syntax for a simple one level navigation. 
{% set navi = craft.entries.section('not projekteintrag' ) %} 
{% for entry in navi.order('id asc') %}
  {{ entry.navigationTitle }}
{% endfor %}


Is there a parameter to just trigger single pages? Right now I grabbed the singles with filtering the entries.
What is the difference between writing the upper syntax and:

-
{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %}
  {{ entry.navigationTitle }}
{% endfor %}

**Tags:** templating, navigation, singles

---

## 7 answers

### Answer by username_7 on 2014-06-12. Score: 19 | Accepted answer

My solution is to setup a dedicated "Menu" section and use an entries field type for the links.  This allows you to pull together entries from multiple sections into a single menu.
My normal setup is to have to custom fields

relatedEntry - Entries field allowing you to select any other entry
customURL - text field for custom url input

The idea being that you could fill in either or and have a dynamic menu with links to entries or custom links.
Then on the template side you simple would do this
{% set menu = craft.entries.section('menu') %}
 
{% nav link in menu %}
    

        {% if link.relatedEntry|length %}
                {{ link.title }}
        {% else %}
                {{ link.title }}
        {% endif %}

        {% ifchildren %}
            
                {% children %}
            
        {% endifchildren %}
     
{% endnav %}


I hope that helps

> **Comments:**
> **username_8**: This is the best answer in my opinion and if you want to ensure it's a single level you need only to set the structure section to a max level of 1 and remove the {% ifchildern %} tags from the above code. Complete flexibility for nav link order, where links go, etc.  
> **username_9**: I have also used this method on 4 sites now and it works well. I just wish we could add the fields to a tab in a field set instead of needing a separate section.  
> **username_0**: How would you add an active class to each link with this configuration?  
> **username_7**: For each link in the loop just add an if craft.request.url == this link  
> **username_1**: The main reason I don't like this approach (other than the performance hit), is that it requires the editor to manage the menu in the 'Entries' area, when intuitively it feels like navigation should be managed in the 'Globals' area.  

---

### Answer by username_1 on 2017-02-01. Score: 14

I pulled together this brain-dump of 8 different ways of doing navigation. There are probably more. With any method make sure you keep a close eye on the query count and load speed, as some of these methods are very resource-intensive. You can mitigate with the cache tag, but ideally should strive for simplicity where possible. Don't be afraid of hardcoding things, it's a totally acceptable solution.
One "mistake" (in my opinion) that I see often in inherited sites is a navigation system that is a direct reflection of the actual content - usually this takes the form of a multi-level "Pages" Structure section that contains everything in the entire site!  Ultimately, your content model should be fully divorced from your actual navigation: for a more thorough monologue about why that's a bad idea in any CMS, not just Craft, see my article, Content Utopia.
nav approaches

Completely hardcoded
Pros: Pure awesome
Cons: Not translatable, no client access
A global table with plain text columns for URI and label (see example code below)
Pros: Light (zero extra queries), flexibility to add any URI
    Cons: No entry selector possible - need to find (and update) URIs manually. No child items (update: actually, you can add a column to mark items as 'level-2'... the template coding to gather them up and build nested lists under the previous parent item is a little complex, but it works well if you only need 2 levels).
Hardcoded but with static translations
Pros: Fairly awesome
Cons: A bit fiddly, and need to translate URIs manually. No client access.
A structure with fields for entry select and arbitrary URL override
Pros: Can accommodate child items easily using craft's nav tag. Intuitive UI.
Cons: Heavy query count, hard to administer all nav items in one go as they're separate entries. Nav management really belongs in Globals IMO, not Entries.
A global matrix with fields for entry select, label override, and arbitrary URL override
Pros: Very flexible
Cons: Heavy query count (8 with eager loading, 10 without). Matrix interface is a bit unwieldy just for a list of links. Can't handle child items without adding a table or supertable block.
A global entries selector
Pros: Nice simple interface. Automatically updating URIs and labels
Cons: Fairly heavy query count. No flexibility to override (unless you put extra fields against the entries). No child items (without adding fields to each entry). No arbitrary items (everything must be an entry).
Hybrid. Depending on the nature of the site, some parts of the nav could be pulled from section entries or category groups while others parts are hardcoded.
Pros: Lots of client control. Changeable dynamic nav.
Cons: Fairly heavy query count (use cache tag). Nav is not fully administered in one single place.
A plugin (ugh)
Cons: it's an unnecessary plugin, just don't do it!

Just in case anyone else likes the idea of #2 above (I think it's becoming my favourite approach personally), here's how you can use that and also cater for a second level of child items. Probably pushes Twig into overly-complex territory, but I'd still rather do this than install a plugin. This approach uses a global table field called primaryNavigation in a global set called navigation with 3 columns: label (text), link (text), level2 (checkbox).
{# ======================================
FIRST, LET's MAKE A HASH MAP to match up 
parents with their children based on their
position in the list
========================================= #}

{% set primaryNav = navigation.primaryNavigation %}
{% set children = {} %}

{% for item in primaryNav %}
    {% set targetArray = {} %}

    {% if item.level2 %}
        {% if attribute(children, lastKnownTopLevelItem) is not defined %}
            {% set children = children|merge({(lastKnownTopLevelItem): [item]}) %}
        {% else %}
            {% set targetArray = attribute(children, lastKnownTopLevelItem) %}
            {% set targetArray = targetArray|merge([item]) %}
            {% set children = children|merge({(lastKnownTopLevelItem): targetArray}) %}
        {% endif %}
    {% endif %}

    {# make a note of the loop index of the last known top level item... #}
    {% if not item.level2 %}
        {% set lastKnownTopLevelItem = 'children-of-' ~ loop.index %}
    {% endif %}
{% endfor %}

{# ======================================
NOW OUTPUT...
========================================= #}


    {# just loop through top level, as we will fetch child items
from the hash map... beware, don't combine the 'not level2'
conditional into the for loop as otherwise the loop indices won't
 match up! #}
    {% for item in primaryNav %}
        {% if not item.level2 %}

            {% set childrenOfThisItem = attribute(children,'children-of-' ~ loop.index) ?? {} %}

            {% if childrenOfThisItem|length %}
                
                    {{ item.label }}
                    
                        {% for child in childrenOfThisItem %}
                            {{ child.label }}
                        {% endfor %}
                    
                
            {% else %}
                {{ item.label }}
            {% endif %}
        {% endif %}
    {% endfor %}


---

### Answer by username_2 on 2014-06-12. Score: 8

In some cases I use a pre-defined (very simple) nav that I don't need to be driven from the CMS yet I want to share it between the header / footer.  In these cases I'll set an array in my base layout template so I can use it throughout.
In "_partials/layout.twig" template:
{% set nav = [
    {'title':'Home', 'link':siteUrl, 'class':'homepage'},
    {'title':'About', 'link':url('about'), 'class':'about-us'},
    {'title':'Contact', 'link':url('contact'), 'class':'contact-us'},
]%}

In "_partials/header.twig" template:

{# logo, etc... #}
{% if (nav is defined) and (nav is iterable) %}
    
    {% for navItem in nav %}
        {{ navItem.title }}
    {% endfor %}
    
{% endif %}


In "_partials/footer.twig" template:
{% if (nav is defined) and (nav is iterable) %}
    
    {% for navItem in nav %}
        {{ navItem.title }}
    {% endfor %}
    
{% endif %}

Very simple concept I know.  But at the end of the day when I know the nav isn't going to change drastically or I don't want the end user to alter it, this is what I do.  It has practically no impact on performance and allows me to manage the items in one place.

> **Comments:**
> **username_0**: That looks very cool, since it is for a super simple Navi.  

---

### Answer by username_3 on 2014-06-12. Score: 7

If you have more complex menus in the future, you should take a look at the Nav-Tag.
.
Getting singles:
There isn't an easy way to get singles (yet). But you can use the same method which is used in crafts backend:
{% set allSections = craft.sections.getAllSections() %}
{% set singleSections = [] %}

{% for section in allSections %}
    {% if section.type == 'single' %}
        {% set singleSections = singleSections|merge([section]) %}
    {% endif %}
{% endfor %}

{% set navi = craft.entries.section(singleSections) %}

.
Queries:
In your case there isn't a difference. But if you plan on using the same query twice, you should save it into a var and reuse it:
This example only makes one db call:
{% set navi = craft.entries.section('not projekteintrag' ).order('id asc') %}

{% for entry in navi %} ... {% endfor %}

{% for entry in navi %} ... {% endfor %}

{% for entry in navi %} ... {% endfor %}

But this makes three:
{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %} ... {% endfor %}

{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %} ... {% endfor %}

{% for entry in craft.entries.section('not projekteintrag' ).order('id asc') %} ... {% endfor %}


---

### Answer by username_4 on 2014-06-12. Score: 4

1) This is not so simple of an answer. Craft itself uses JS to handle the content of indexes, so I'm not sure how they're doing this. Using what's available you would need to build a list of Single Sections, then pull entries based on that list.
{% set sections = [] %}
{% for section in craft.sections.getAllSections() %}
    {% if section.type == 'single' %}
        {% set sections = sections|merge([section]) %}
    {% endif %}
{% endfor %}

Then your query would be (however you choose to use it):
craft.entries.section(sections).order('id asc')

2) Both examples are the same. You can keep adding/editing criteria until you actually pull the entries by using an output function (find, first, etc) or using it in a loop (such as for and nav)

> **Comments:**
> **username_0**: What does [] in the first variable do?  
> **username_4**: It's an empty array. Later we add our section to it my making it a single-item array and merging them together.  

---

### Answer by username_5 on 2016-02-29. Score: 4

Another easy way to handle a dynamic menu in Craft is to add a Matrix field to a Global.
For example, I have a Global called "Main Menu". That Global has a Matrix field called "Menu Item". I then have a block called "Item" that has two plain text fields. One field is for the "label" and one field is for the "URI" that I want users to be directed to.
Then in my template file I have the following.
{% for block in mainMenu.menu %}
    {% if block.type == "item" %}
      
        {{ block.label }}
       
    {% endif %}
{% endfor %}

> **Comments:**
> **username_1**: If you're only using plain text fields, you can do this more efficiently with a table fieldtype instead of a matrix. The matrix approach would be if you want to use an entries selector to get the URIs (however, I've found that to be highly inefficient, adding an extra 10 queries per page load)  

---

### Answer by username_6 on 2019-10-17. Score: 2

This doesn't work for every project, but for brochure style sites I like having all the client-arrangeable pages in one Structure, with toggles on each entry to optionally show in primary or secondary navigation, and Entry Types for different requirements such as a team page or articles section, and reserving Singles for things like homepage, privacy policy etc. It makes things very intuitive for clients to get the hang of (even though it makes it easier for them to potentially break things).


---

