---
title: "Adding Tabbed Navigation to Portals"
slug: "portal-tabs"
updated: 2026-04-16T19:03:21Z
published: 2026-04-16T19:03:21Z
canonical: "knowledge.technolutions.net/portal-tabs"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://knowledge.technolutions.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Portal Tabs

Adding **tabs**to a portal lets you divide information among interactive sections, which cuts down on scrolling and can improve the user experience.

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Portal with tabs.gif)

#### Steps to creating a portal with tabs

You’ll make a portal with tabs by following these steps:

1. Create a default view that holds the tab objects and any content that appears on all tabs, then a view for each tab.
2. Create a method for each of these views.
3. Create the tab framework in the default view using HTML (we provide the scaffolding).
4. Add the JavaScript we provide below this HTML to asynchronously load tab content.

After these steps, we show you how to:

- [style your tabs with CSS](/v1/docs/portal-tabs#styling-portal-tabs-with-css)
- [display tabs conditionally](/v1/docs/portal-tabs#displaying-tabs-conditionally)
- [get analytics scripts to report tab clicks as unique visits](/v1/docs/portal-tabs#analytics-for-tabbed-portals)

## Step 1: Creating views

Portals use **views** to display content. On a portal with tabs, each tab is represented by a view.

There is also a **default view**, which contains the tab menu items and any content that should appear no matter what tab is selected.

> [!NOTE]
> ➡️ Find steps for creating and configuring portal views in [Creating a Custom Portal → Creating portal views](/docs/creating-a-custom-portal#step-4-creating-portal-views).

#### Creating the default view

The **default view**contains code that creates selectable tag objects (which we’ll be adding later), plus any content you’d like to appear regardless of which tab is selected.

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/The default view rendering tab objects and static content that appears on all tabs.png)

The default view is distinct from a *Home* tab (one that loads first before any selection is made).

#### Tab view layouts

Most tab views should use a **one-column layout**, particularly if the default view is a two-column or another partitioned layout. Avoid embedding a multi-column view in another a multi-column view.

#### Moving existing content into new views

If you’re adding tabs to an existing portal, you can move portal parts from one view to another with the **Move** button.

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Move portal part to new view.png)

With a default view and a view for each tab created, we can make **methods** for each view.

## Step 2: Creating methods

For each view, including the default view, we’ll create a corresponding **method**. We create a method to associate a tab object (which we create later) with a view.

When that tab is selected, it calls the method we’ve given it, which, with help from some JavaScript we provide later, loads the specified view.

Methods can also pull query results into the view, among other things.

> [!NOTE]
> ➡️ Find steps for creating portal methods in [Creating a Custom Portal → Adding portal methods](/docs/creating-a-custom-portal#step-2-adding-portal-methods).

Create a method called **Default** and configure the following settings:

- **Name:**An internal name for the method, like *Default*.
- **Type:**GET
- **Output Type:**Default Branding
- **Action:**Leave this field blank.
- **View:**Select the default tab view.

For each **tab view** that you created in the previous section, create a corresponding method****and****configure the following settings:

- **Name:**An internal name for the method. It’s often a good idea to give it the same name as the associated view.
- **Type:**GET
- **Output Type:**AJAX Popup/No Branding
- **Action:**Enter a computer-friendly (lowercase letters and underscores only) name with a maximum of 32 characters. This value appear later, in the tab framework code, and also in the URL when the tab is selected.
- **View:**Select the associated tab view you created in the previous step.

In this example, the **Tab: Home** view is associated with a method named **Tab: Home** and an **Action** of `home`.

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Portal tab methods(1).png)

## Step 3: Creating the tab framework on the default view

In this step, we add an HTML scaffold using a `subtabs` list object that renders as the tabs at the top of the default view page.

Each item has a `data-tab` HTML attribute; for example, `data-tab="home"`. This attribute values reference the **Action**of the Home method we created in the previous section.

We then replace the placeholder values with method actions and tab labels.

In the default view:

1. Select a **Static Content block** from the palette.
2. Configure the following settings:
  - **Name:**Enter an internal name for the block.
  - **Status:**Active
  - **CSS Class Name:**Enter a CSS class name for targeting by your institutional branding, if applicable.
3. Select **Source.**
4. Copy the following placeholder HTML and paste it in the source editor:

```xml
<ul class="subtabs">
    <li>
        <a href="#" data-tab="method_action_1">Tab Label 1</a>
    </li>
    <li>
        <a href="#" data-tab="method_action_2">Tab Label 2</a>
    </li>
    <li>
        <a href="#" data-tab="method_action_3">Tab Label 3</a>
    </li>
</ul>
```
  - This code creates an HTML unordered list `&lt;ul&gt;` with the class `subtabs`. Each list item `&lt;li&gt;` renders as a tab.
  - In each list item, there is an anchor `&lt;a&gt;` tag with the attribute `data-tab`. The value between the quotes references a **method action**.
  - The tab label is represented by the text inside the anchor tag.
5. For each list item `&lt;li&gt;`, replace the **placeholder value inside the quotes**for `data-tab` (`method_action_1`, `method_action_2`, …) with the **action**of the method that corresponds with the view you want to appear when that tab is selected.
6. Replace the **placeholder tab label (**`Tab Label 1`, `Tab Label 2`, …) with the name you want to appear on the tab.

Here’s an example of how your HTML list might look:

```xml
<ul class="subtabs">
    <li>
        <a href="#" data-tab="home">Home</a>
    </li>
    <li>
        <a href="#" data-tab="profile">Profile</a>
    </li>
    <li>
        <a href="#" data-tab="directory">Directory</a>
    </li>
</ul>      
```

## Step 4: Adding JavaScript

When a user selects a tab, we don’t want that action to reload the entire page. Instead, we’ll use some **JavaScript**to load the tab content, render it on the page dynamically, and update the current URL.

Note that this code also contains some HTML: the `div` element with the ID `content_body` is a placeholder element which will contain the tab content once the JavaScript runs.

In the default view, in the source editor of the static content block we created in the previous step:

1. Select **Source.**
2. Copy the following code:

```xml
<div id="content_body">
</div>
<script type="text/javascript">
/*<![CDATA[*/
var loadTab = function (tab, queryString, isBack) {
if (queryString) {
var qs = FW.decodeFormValues(queryString);
delete qs["tab"];
queryString = FW.encodeFormValues(qs);
queryString = queryString ? "&" + queryString : "";
}
if (!isBack) history.pushState(tab, null, "?tab=" + tab + queryString);
$("a[data-tab]").removeClass("active");
$("a[data-tab='" + tab + "']").addClass("active");
FW.Lazy.Fetch("?cmd=" + tab + queryString, $("#content_body"));
};

window.addEventListener("popstate", function (e) {
if (e.state) loadTab(e.state, location.search.substring(1), true);
else history.back();
});

$("a[data-tab]").on("click", function () {
loadTab($(this).data("tab"), location.search.substring(1));
return false;
});

var qs = FW.decodeFormValues(location.search.substring(1));
if (qs["tab"]) loadTab(qs["tab"], location.search.substring(1));
else {
var default_tab = $("ul.subtabs").find("li > a").eq(0).data("tab");
loadTab(default_tab, location.search.substring(1));
}
/*]]>*/</script>
```
3. Paste the code below the `&lt;ul&gt;` list configured in the previous section.
4. Select **Save.**

**Test your portal:**you should see the tabs rendering on the default view. If you don’t, double check the formatting of the HTML list you configured in the previous section.

## Styling portal tabs with CSS

You can customize the presentation of portal tabs in **Edit → Custom CSS Rules.**

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Custom CSS rules.png)

#### How Slate renders portal tabs

In the portal view, tabs are added to the source code as list items (`&lt;li&gt;`) in an unordered list (`&lt;ul&gt;`).

The `&lt;ul&gt;` element has a class attribute of `subtabs`. Each individual list item itself contains an anchor (`&lt;a&gt;`) element, which displays the name of the tab. Each anchor element also has `data-tab` attribute values that correspond to that particular tab method's action.

The `active`****class is added to the anchor representing the current tab being viewed. For example, the *Home*tab by default upon first entering the portal, or the tab that’s currently selected.

The tabs (specifically, `&lt;ul&gt;` elements with a class of `subtabs`, and nested or child `&lt;li&gt;` and `&lt;a&gt;` elements) are styled by default CSS as part of Slate standard branding.

#### Targeting portal tabs with CSS rules

**Set style for all content in portal tabs object:**

```css
#content ul.subtabs li a {height: auto; font-size: 14px; text-align: center;}
```

**Set a margin-top of 0:**

```css
.part ul.subtabs {margin: 0 0 1em; width:100%;}
```

**Adjust tab width:**

```css
.part ul.subtabs li {width:20%;}
```

**Modify tab colors, padding, margin, and apply rounded corners:**

```css
.part ul.subtabs li a {background-color: #f5f5f5; color: #000000; padding: 5px 15px; border-radius: 3px; margin-right: 7px;}
```

**Modify color of the selected (active) tab:**

```css
.part ul.subtabs li a.active {color: #fff !important; background-color: #00669e;}
```

**Modify color of a tab upon mouse hover:**

```css
.part ul.subtabs li a:hover {background-color: #fafafa !important; color: #00669e !important;}
```

If a custom CSS rule doesn't appear to have any effect, you could try adding the `!important` keyword (e.g. `{ width: 20% !important; }`), and/or change the CSS selector so that it becomes more specific to the element you're trying to update (e.g. `#content ul.subtabs li a`).

To see the changes on a more immediate basis, sometimes you might also need to do a hard refresh on your browser (Chrome PC: `Ctrl + R`/Chrome Mac: `CMD + SHIFT + R`).

> [!TIP]
> ✨ Tip: Use your browser’s inspect tool (Right click → Inspect) to see how `ul.subtabs` elements are rendered on the page.

## Displaying tabs conditionally

You can configure a tab to appear only if the user meets certain criteria:

1. Create a query in the portal.
2. Add an **existence subquery export**that checks for your criteria.
3. In the *Default*method, select **Edit Linked Queries**and select the query you created.
4. In the default view, add Liquid markup in the source editor to the `&lt;li&gt;` tag you want to conditionally display.
5. Prevent the tab from rendering if the tab URL is entered but the end-user doesn’t meet the criteria.

The following example provides a more detailed walkthrough.

📖 **Further reading**: [Liquid markup](/v1/docs/getting-started-with-liquid-markup)

#### Example: Hide *Assignments*tab from non-captain alumni interviewers

In an alumni interview portal, you may only want to show an *Assignments*tab to users with an assigned role of *Captain*.

We create a new query in the portal. To it, we add an existence subquery export called `captain` with a filter for records with an alumni role `IN` Captain.

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Screenshot 2024-09-03 at 3.32.10 PM.png)

In the *Default* method, we associate this query with the default view.

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Link portal method query to default view.png)

In the **default view**, we edit the source code and place a Liquid markup `if` tag around the `&lt;li&gt;` tags that looks for the existence subquery export’s *Value If Exists*value:

```xml
<ul class="subtabs">
    <li>
        <a data-tab="home" href="#">Home</a>
    </li>
	{% if captain == 'Y' %}
    <li>
        <a data-tab="assignments" href="#">Assignments</a>
    </li>
	{% endif %}
</ul>
```

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Screenshot 2024-09-03 at 3.29.40 PM.png)

Finally, in the *Tab: Assignments* view, we wrap the *everything*in the source code with our Liquid markup `if` tag from earlier. If someone were to attempt to view this tab by editing the URL or using an old bookmark, nothing would render on the page.

![](https://cdn.us.document360.io/cd8ea7a6-07f3-4846-a554-627ac016d3e3/Images/Documentation/Screenshot 2024-09-03 at 3.37.03 PM.png)

📖 **Further reading**: [Slate standard alumni interviewing portal](/v1/docs/slate-standard-alumni-interviewing-portal)

#### Alternative methods for conditional tab display

Another approach involves a **portal redirect**. If your tab view has multiple widgets or it would otherwise be impractical to wrap the entire tab in Liquid markup, a redirect can prevent the tab from ever loading in the first place.

📖 **Further reading**: [Portal redirects](/docs/portal-redirects)

## Analytics for tabbed portals

Portals that make use of tabs will not report navigation between tabs to analytics tools without additional configuration.

Activity is reported upon the first visit to a portal, but subsequent navigation between tabs does not count as a fresh page load and will not trigger analytics scripts placed in website branding.

One potential workaround involves placing tracking codes within the views for each tab. Tracking codes embedded within individual tabs or pages should be reported as unique visits to analytics scripts.

For example, adding a static content block to a portal’s *Visit*tab and embedding a tracking code within the source code may enable you to see the number of visits specific to this tab. However, different tracking codes may behave differently, so the process should be tested thoroughly.

It may not be possible in every case to return analytics for individual tab views.

📖 **Further reading**: [External analytics scripts](/v1/docs/external-analytics-scripts)

## Related

- [The For Tag and Liquid Looping](/liquid-markup-looping.md)
