Spina CMS
Spina Academy
Home Guides

Adding navigation to your website

One of the first things you'll want to do is add navigation menus to your website. You can use Spina's built-in MenuPresenter or write something by hand. 

Pages & navigations

One of the things you'll notice is that Spina has pages, but also a separate model for navigations. Basically, navigations allow you to create separate menus with their own structure. This might be useful if your website has a main navbar, but a different list of links in it's footer. You can use navigations to create different structures for these sections. 

If your website doesn't have multiple menus, or if you simply don't care about managing different menus from the admin UI, feel free to just skip the Navigation model and stick to working with the Page model directly.

Using MenuPresenter

You can use the following partial to quickly generate a menu based on all of your pages. The MenuPresenter will automatically handle your page ordering and skip drafts.
<!-- app/views/shared/_navigation.html.erb -->
<% presenter = Spina::MenuPresenter.new(Spina::Page.all) %>
<%= presenter.to_html %>
By default, this presenter generates the following HTML:
<nav>
  <ul>
    <li data-page-id="500">
      <a href="/">Homepage</a>
    </li>
    <li data-page-id="11">
      <a href="/page">Page</a>
    </li>
    <li data-page-id="2503">
      <a href="/about-us">About us</a>
      <ul>
        <li data-page-id="2504">
          <a href="/about-us/our-business">Our business</a>
        </li>
        <li data-page-id="2505">
          <a href="/about-us/our-story">Our story</a>
        </li>
      </ul>
    </li>
    <li data-page-id="2506">
      <a href="/contact">Contact</a>
    </li>
  </ul>
</nav>
The MenuPresenter object has a couple of configurable attributes to customize the HTML output:
:menu_tag # default :nav
:menu_css
:list_tag # default :ul
:list_css
:list_item_tag # default :li
:list_item_css
:link_tag_css
:active_list_item_css
:current_list_item_css
:include_drafts # default false
:depth # root nodes are at depth 0
Instead of initializing a MenuPresenter with Page-records, you can also use a Navigation object:
<!-- app/views/shared/_navigation.html.erb -->
<% navigation = Spina::Navigation.find_by(name: "main")
<% presenter = Spina::MenuPresenter.new(navigation) %>
<%= presenter.to_html %>
Make sure you've added a Navigation in your theme config first.

Manually creating a menu

If you need more control over your navigation structure, you can manually create a list like the following example. It's important to use all required scopes.
<!-- app/views/shared/_navigation.html.erb -->
<% Spina::Page.roots.regular_pages.live.sorted.in_menu.each do |page| %>
  <%= link_to page.menu_title, page.materialized_path, class: ("active" if request.path.starts_with?(page.materialized_path)) %>
  <div class="submenu">
    <% page.children.sorted.in_menu.live.each do |child_page| %>
      <%= link_to child_page.menu_title, child_page.materialized_path %>
    <% end %>
  </div>

<% end %>
Here's another example using a Navigation record instead:
<!-- app/views/shared/_navigation.html.erb -->
<% nav = Spina::Navigation.find_by(name: "main") %>
<% nav.navigation_items.regular_pages.roots.live.sorted.in_menu.each do |nav_item| %>
  <%= link_to nav_item.menu_title, nav_item.materialized_path, class: ("active" if request.path.starts_with?(nav_item.materialized_path)) %>
  <div class="submenu">
    <% nav_item.children.sorted.in_menu.live.each do |child_nav_item| %>
      <%= link_to child_nav_item.menu_title, child_nav_item.materialized_path %>
    <% end %>
  </div>

<% end %>