diff --git a/brian_dashboard_manager/templating/helpers.py b/brian_dashboard_manager/templating/helpers.py
index 9c8d6e56623aa6b3d95b0f3004c37021aac8a82e..89ca0c847ec4587b57c9eab402b3959c9f13b64c 100644
--- a/brian_dashboard_manager/templating/helpers.py
+++ b/brian_dashboard_manager/templating/helpers.py
@@ -744,7 +744,7 @@ def default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=Tr
     :return: function that generates panel definitions
     """
 
-    def get_panel_definitions(panels, datasource, errors=False):
+    def get_panel_definitions(panel_data, datasource, errors=False):
         """
         Generates the panel definitions for the dashboard based on the
         panel data for the panel types (traffic, errors, IPv6).
@@ -760,21 +760,21 @@ def default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=Tr
         """
         result = []
 
-        for panel in panels:
+        for interface in panel_data:
             if use_all_traffic:
                 result.append(get_panel_fields({
-                    **panel,
+                    **interface,
                     **next(gridPos)
                 }, 'traffic', datasource))
             if use_ipv6:
-                if panel.get('has_v6', False):
+                if interface.get('has_v6', False):
                     result.append(get_panel_fields({
-                        **panel,
+                        **interface,
                         **next(gridPos)
                     }, 'IPv6', datasource))
             if errors:
                 result.append(get_panel_fields({
-                    **panel,
+                    **interface,
                     **next(gridPos)
                 }, 'errors', datasource))
 
@@ -901,7 +901,7 @@ def get_aggregate_dashboard_data(title, remotes, datasource, tag):
     return result
 
 
-def get_nren_dashboard_data_single(data, datasource, tag):
+def get_dashboard_with_agg_data_single(data, datasource, tag):
     """
     Helper for generating dashboard definitions for a single NREN.
 
@@ -917,14 +917,14 @@ def get_nren_dashboard_data_single(data, datasource, tag):
     :return: dashboard definition for the NREN dashboard
     """
 
-    nren, dash = data
+    dashboard_name, dashboard_data = data
     id_gen = num_generator()
 
-    if len(dash['AGGREGATES']) > 0:
+    if len(dashboard_data.get('AGGREGATES', [])) > 0:
         agg_panels = create_aggregate_panel(
-            f'Aggregate - {nren}',
+            f'Aggregate - {dashboard_name}',
             gridPos_generator(id_gen, agg=True),
-            dash['AGGREGATES'], datasource)
+            dashboard_data['AGGREGATES'], datasource)
         gridPos = gridPos_generator(id_gen, start=2)
     else:
         gridPos = gridPos_generator(id_gen)
@@ -933,113 +933,50 @@ def get_nren_dashboard_data_single(data, datasource, tag):
     panel_gen = default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=False)
     panel_ipv6_gen = default_interface_panel_generator(gridPos, use_all_traffic=False, use_ipv6=True)
 
-    services_dropdown = create_dropdown_panel('Services', **next(gridPos))
-
     def sort_key(panel):
         sort = panel.get('sort')
         if not sort:
             return 'ZZZ' + panel.get('hostname')  # sort to end
         return sort
 
-    service_panels = panel_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource)
-
-    services_ipv6_dropdown = create_dropdown_panel('Services - IPv6 Only', **next(gridPos))
-    service_ipv6_panels = panel_ipv6_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource
-    )
-
-    iface_dropdown = create_dropdown_panel('Interfaces', **next(gridPos))
-    phys_panels = panel_gen(dash['PHYSICAL'], datasource, True)
-
-    dropdown_groups = [{
-        'dropdown': services_dropdown,
-        'panels': service_panels,
-    }]
-    if len(service_ipv6_panels) > 0:
-        dropdown_groups.append({
-            'dropdown': services_ipv6_dropdown,
-            'panels': service_ipv6_panels
-        })
-    dropdown_groups.append({
-        'dropdown': iface_dropdown,
-        'panels': phys_panels,
-    })
-
-    result = {
-        'nren_name': nren,
-        'datasource': datasource,
-        'aggregate_panels': agg_panels,
-        'dropdown_groups': dropdown_groups
-    }
-    if isinstance(tag, list):
-        result['tags'] = tag
-    else:
-        result['tag'] = tag
-
-    return result
-
-
-def get_re_peer_dashboard_data_single(data, datasource, tag):
-    """
-    Helper for generating dashboard definitions for a single R&E Peer.
-
-    NREN dashboards have two aggregate panels (ingress and egress),
-    and two dropdown panels for services and interfaces.
-
-    :param data: data for the dashboard, including the R&E Peer name and
-    the panel data
-    :param datasource: datasource to use for the panels
-    :param tag: tag to use for the dashboard, used for dashboard dropdowns on
-    the home dashboard.
-
-    :return: dashboard definition for the R&E Peer dashboard
-    """
-
-    peer, dash = data
-    id_gen = num_generator()
+    services_dropdown = create_dropdown_panel('Services', panels=[], **next(gridPos), collapsed=False)
+    service_panels = panel_gen(sorted(dashboard_data.get('SERVICES', []), key=sort_key), datasource)
 
-    if len(dash['AGGREGATES']) > 0:
-        agg_panels = create_aggregate_panel(
-            f'Aggregate - {peer}',
-            gridPos_generator(id_gen, agg=True),
-            dash['AGGREGATES'], datasource)
-        gridPos = gridPos_generator(id_gen, start=2)
-    else:
-        gridPos = gridPos_generator(id_gen)
-        agg_panels = []
+    service_ipv6_panels = panel_ipv6_gen(sorted(dashboard_data.get('SERVICES', []), key=sort_key), datasource)
+    services_ipv6_dropdown = create_dropdown_panel(
+        'Services - IPv6 Only', panels=service_ipv6_panels, **next(gridPos), collapsed=True)
 
-    panel_gen = default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=True)
+    phys_panels = panel_gen(dashboard_data.get('PHYSICAL', []), datasource, errors=True)
+    iface_dropdown = create_dropdown_panel('Interfaces', panels=phys_panels, **next(gridPos), collapsed=True)
 
-    services_dropdown = create_dropdown_panel('Services', **next(gridPos))
+    panels = []
 
-    def sort_key(panel):
-        sort = panel.get('sort')
-        if not sort:
-            return 'ZZZ' + panel.get('hostname')  # sort to end
-        return sort
+    num_dropdowns = sum(1 for p in [service_panels, service_ipv6_panels, phys_panels] if len(p) > 0)
 
-    service_panels = panel_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource)
+    if len(service_panels) > 0 and num_dropdowns > 1:
+        panels.append(services_dropdown)
+        panels.extend(service_panels)
 
-    iface_dropdown = create_dropdown_panel('Interfaces', **next(gridPos))
-    phys_panels = panel_gen(dash['PHYSICAL'], datasource, True)
+    if len(service_ipv6_panels) > 0 and num_dropdowns > 1:
+        panels.append(services_ipv6_dropdown)
 
-    dropdown_groups = [{
-        'dropdown': services_dropdown,
-        'panels': service_panels,
-    }]
+    if len(phys_panels) > 0 and num_dropdowns > 1:
+        panels.append(iface_dropdown)
 
-    dropdown_groups.append({
-        'dropdown': iface_dropdown,
-        'panels': phys_panels,
-    })
+    if num_dropdowns <= 1:
+        # if there is only one dropdown, just add all the panels instead
+        if service_panels:
+            panels.extend(service_panels)
+        elif service_ipv6_panels:
+            panels.extend(service_ipv6_panels)
+        elif phys_panels:
+            panels.extend(phys_panels)
 
     result = {
-        'nren_name': peer,
+        'title': dashboard_name,
         'datasource': datasource,
         'aggregate_panels': agg_panels,
-        'dropdown_groups': dropdown_groups
+        'panels': panels
     }
     if isinstance(tag, list):
         result['tags'] = tag
@@ -1049,48 +986,35 @@ def get_re_peer_dashboard_data_single(data, datasource, tag):
     return result
 
 
-def get_service_dashboard_data_single(data, datasource, tag):
+def get_dashboard_data_single(
+        data, datasource, tag,
+        panel_generator=default_interface_panel_generator,
+        errors=False):
     """
-    Helper for generating dashboard definitions for a single service.
+    Helper for generating dashboard definitions for non-NREN dashboards.
 
-    :param data: data for the dashboard, including the service name and
+    :param data: data for the dashboard, including the dashboard name and
     the panel data
     :param datasource: datasource to use for the panels
     :param tag: tag to use for the dashboard, used for dashboard dropdowns on
     the home dashboard.
+    :param panel_generator: function for generating panel definitions
+    :param errors: whether or not to include an error panel for each interface
 
-    :return: dashboard definition for the service dashboard
+    :return: dashboard definition for the NREN dashboard
     """
 
-    service_name, dash = data
     id_gen = num_generator()
-
     gridPos = gridPos_generator(id_gen)
+    panel_gen = panel_generator(gridPos)
 
-    panel_gen = default_interface_panel_generator(gridPos, use_all_traffic=True, use_ipv6=False)
-
-    services_dropdown = create_dropdown_panel('Services', **next(gridPos))
-
-    def sort_key(panel):
-        sort = panel.get('sort')
-        if not sort:
-            return 'ZZZ' + panel.get('hostname')  # sort to end
-        return sort
-
-    service_panels = panel_gen(
-        sorted(dash['SERVICES'], key=sort_key), datasource)
-
-    dropdown_groups = [{
-        'dropdown': services_dropdown,
-        'panels': service_panels,
-    }]
-
+    name, panel_data = data
     result = {
-        'nren_name': service_name,
+        'title': name,
         'datasource': datasource,
-        'aggregate_panels': [],
-        'dropdown_groups': dropdown_groups
+        'panels': panel_gen(panel_data, datasource, errors),
     }
+
     if isinstance(tag, list):
         result['tags'] = tag
     else:
@@ -1099,15 +1023,15 @@ def get_service_dashboard_data_single(data, datasource, tag):
     return result
 
 
-def get_dashboard_data_single(
+def get_dashboard_data_dropdown_single(
         data, datasource, tag,
         panel_generator=default_interface_panel_generator,
         errors=False):
     """
-    Helper for generating dashboard definitions for non-NREN dashboards.
+    Helper for generating dashboard definitions for dashboards with dropdowns.
 
     :param data: data for the dashboard, including the dashboard name and
-    the panel data
+    the dropdown groups with each group containing panel data
     :param datasource: datasource to use for the panels
     :param tag: tag to use for the dashboard, used for dashboard dropdowns on
     the home dashboard.
@@ -1121,13 +1045,20 @@ def get_dashboard_data_single(
     gridPos = gridPos_generator(id_gen)
     panel_gen = panel_generator(gridPos)
 
-    name, panels = data
+    name, panel_groups = data
+
+    panels = []
     result = {
         'title': name,
         'datasource': datasource,
-        'panels': list(panel_gen(panels, datasource, errors)),
+        'panels': panels
     }
 
+    for group, _panels in panel_groups.items():
+        dropdown = create_dropdown_panel(group, panels=list(
+            panel_gen(_panels, datasource, errors)), collapsed=True, **next(gridPos))
+        panels.append(dropdown)
+
     if isinstance(tag, list):
         result['tags'] = tag
     else:
@@ -1160,10 +1091,34 @@ def get_dashboard_data(
     yield from map(func, data.items())
 
 
+def get_dashboard_data_dropdown(
+        data, datasource, tag,
+        panel_generator=default_interface_panel_generator,
+        errors=False):
+    """
+    Helper for generating dashboard definitions for interface-based non-NREN dashboards.
+
+    :param data: the dashboard names and the panel data for each dashboard
+    :param datasource: datasource to use for the panels
+    :param tag: tag to use for the dashboard, used for dashboard dropdowns on
+    the home dashboard.
+    :param panel_generator: function for generating panel definitions
+    :param errors: whether or not to include an error panel for each interface
+
+    :return: generator for dashboard definitions for each dashboard
+    """
+
+    func = partial(
+        get_dashboard_data_dropdown_single,
+        datasource=datasource, tag=tag, panel_generator=panel_generator, errors=errors)
+
+    yield from map(func, data.items())
+
+
 def get_nren_dashboard_data(data, datasource, tag):
 
     func = partial(
-        get_nren_dashboard_data_single,
+        get_dashboard_with_agg_data_single,
         datasource=datasource,
         tag=tag)
 
@@ -1172,7 +1127,7 @@ def get_nren_dashboard_data(data, datasource, tag):
 
 def get_re_peer_dashboard_data(data, datasource, tag):
     func = partial(
-        get_re_peer_dashboard_data_single,
+        get_dashboard_with_agg_data_single,
         datasource=datasource,
         tag=tag)
 
@@ -1181,7 +1136,7 @@ def get_re_peer_dashboard_data(data, datasource, tag):
 
 def get_service_dashboard_data(data, datasource, tag):
     func = partial(
-        get_service_dashboard_data_single,
+        get_dashboard_with_agg_data_single,
         datasource=datasource,
         tag=tag)
 
diff --git a/brian_dashboard_manager/templating/render.py b/brian_dashboard_manager/templating/render.py
index afb65e305a00404ce9cd3194bf316a6bef773062..396aab49cd9bea9eed17355687c6763438216528 100644
--- a/brian_dashboard_manager/templating/render.py
+++ b/brian_dashboard_manager/templating/render.py
@@ -1,6 +1,5 @@
 BASE_DROPDOWN_PANEL = {
     "aliasColors": {},
-    "collapsed": False,
     "datasource": None,
     "fill": None,
     "fillGradient": None,
@@ -121,7 +120,7 @@ INFOBOX = {
 }
 
 
-def create_dropdown_panel(title, id, y, **kwargs):
+def create_dropdown_panel(title, id, y, panels, collapsed=True, **kwargs):
     """
     Creates a dropdown panel from the given data.
 
@@ -132,11 +131,18 @@ def create_dropdown_panel(title, id, y, **kwargs):
 
     :return: rendered dropdown panel JSON
     """
+
+    # If collapsed is false, panels should come after the dropdown panel, not inside it
+    if not collapsed and panels:
+        raise ValueError("Collapsed dropdown panels cannot have panels, add them after the dropdown panel.")
+
     return {
         **BASE_DROPDOWN_PANEL,
+        "collapsed": collapsed,
         "id": id,
         "gridPos": {"h": 1, "w": 24, "x": 0, "y": y},
         "title": title,
+        "panels": panels,
     }
 
 
@@ -313,31 +319,31 @@ def create_panel(
     return result
 
 
-def render_with_aggregate_dashboard(
-    nren_name, aggregate_panels, dropdown_groups, tag=None, tags=None, **_
-):
+def render_with_aggregate_dashboard(title, aggregate_panels, panels, tag=None, tags=None, infobox=True, **_):
     assert tag or tags
-    panels = [INFOBOX, *aggregate_panels]
-    for group in dropdown_groups:
-        panels.append(group["dropdown"])
-        panels.extend(group["panels"])
+    if infobox:
+        panels = [INFOBOX, *aggregate_panels, *panels]
+    else:
+        panels = [*aggregate_panels, *panels]
 
     return {
         **BASE_DASHBOARD,
         "tags": tags or [tag],
-        "title": nren_name,
+        "title": title,
         "panels": panels,
     }
 
 
-def render_simple_dashboard(title, tag=None, tags=None, panels=None, **_):
+def render_simple_dashboard(title, tag=None, tags=None, panels=None, infobox=True, **_):
     assert tag or tags
+    if infobox:
+        panels = [INFOBOX, *(panels or [])]
+    else:
+        panels = panels or []
+
     return {
         **BASE_DASHBOARD,
         "tags": tags or [tag],
         "title": title,
-        "panels": [
-            INFOBOX,
-            *(panels or []),
-        ],
+        "panels": panels,
     }