- Tabbed interfaces organize multiple views or documents in a single window using clear, switchable sections.
- Android TabLayout, Elementor nested tabs and Bootstrap nav tabs cover most in-page tab use cases.
- PWA tabbed application mode brings system-level document tabs to Progressive Web Apps on ChromeOS.
- Good tab design limits items, uses clear icons and maintains consistent, always-visible navigation.
Tabbed interfaces are one of the most familiar navigation patterns in modern software, from mobile apps and web dashboards to complex development tools. They let users jump between different sections or documents inside the same window, keeping the experience tidy, fast to scan and easy to learn. When they are thoughtfully designed, tabs dramatically reduce clutter and make it clear what content is currently visible.
Behind that simple row of labels there are many design decisions and technical options: fixed or scrollable tabs, icon-only layouts, nested tab systems in page builders, document-style tabbed windows in Progressive Web Apps, or custom tab widgets built with frameworks like Bootstrap or Xajax. In this guide we are going to walk through a wide range of tabbed interface examples and implementation approaches, pulling together all the ideas from Android, web UI, PWA manifests and classic PHP + Ajax solutions.
Tabbed interfaces in Android with TabLayout and ViewPager
On Android, the main building block for tabbed navigation in Material Design is the TabLayout widget, often paired with a ViewPager (or ViewPager2 in newer projects). Google’s own Material Design team describes tabs very simply: they make it easy to explore and switch between different views inside the same activity.
A TabLayout arranges tabs horizontally and exposes two main operating modes: fixed and scrollable. In fixed mode, every tab is visible at the same time and the available width is divided between them. This is the pattern used in apps like WhatsApp, where a handful of distinct sections are always one tap away and the tabs fill the entire row.
Scrollable tabs come into play when the number of sections or the length of the labels exceeds the screen width. In this configuration, the user can swipe horizontally across the tab bar to uncover more options. Google’s own News & Weather app is a good reference for a scrollable TabLayout, where content categories outgrow what a single screen can host comfortably.
Each tab in a TabLayout usually maps to a Fragment shown inside a ViewPager. When the user taps on a tab, TabLayout updates its selected state and the ViewPager navigates to the corresponding fragment. Likewise, when the user swipes between pages, the selected tab indicator moves to track the currently visible fragment. This tight coupling is normally wired up via setupWithViewPager(), which automatically creates tabs and hooks up listeners for both scroll and click events.
Material tabs can display text, icons or a combination of both. Some apps, like Twitter on Android, lean heavily on recognizable icons instead of labels, which saves space and can feel faster to scan once users understand the meaning of each symbol. TabLayout supports all of these options, whether defined in XML or set programmatically.
Creating a basic Android tabbed interface step by step
To build a simple tabbed UI from scratch in Android Studio, you typically start with an empty activity, often called something like MainActivity, and then introduce three key pieces: a handful of fragments, a TabLayout in your layout XML, and a ViewPager managed by a pager adapter.
The first building block is a small set of Fragment classes, one per tab. For example, you might define FragmentOne, FragmentTwo and FragmentThree, each inflating its own XML layout (for example, fragment_one.xml). The code for each fragment is straightforward: inflate the view, bind any widgets and supply the content relevant to that tab.
Next you add TabLayout and ViewPager to your main activity layout. In activity_main.xml you declare a TabLayout element with an ID like tab_layout and a ViewPager right below it. With attributes such as app:tabMode and app:tabGravity you control whether tabs are fixed or scrollable, and whether they stretch to fill the available width. Setting tabMode="fixed" and tabGravity="fill" will distribute all tabs evenly across the bar, which is especially noticeable on wide screens like tablets.
Styling tabs is usually done with a custom style reference on the TabLayout. Through a style like @style/CustomTabLayout, you can define things such as the indicator color (tabIndicatorColor), indicator height (tabIndicatorHeight) and text colors for selected and unselected states (tabTextColor and tabSelectedTextColor). The same properties can also be set programmatically with methods like setSelectedTabIndicatorColor() or setTabTextColors(), but centralizing the design in XML makes it easier to keep the look consistent.
After defining the layout, you attach a pager adapter that controls which Fragment appears for each tab. A common implementation extends FragmentPagerAdapter (or FragmentStatePagerAdapter or the newer FragmentStateAdapter for ViewPager2) and overrides three methods: getItem() to supply the fragment for a specific position, getCount() to report how many pages exist, and getPageTitle() to deliver the label text for each tab. When the first tab, titled something like “Tab Item 1”, is selected, getItem() will return FragmentOne, linking the label to its content.
All the pieces come together in the activity’s onCreate() method. There you grab references to the TabLayout and ViewPager from activity_main.xml, construct your adapter with the FragmentManager and set it on the ViewPager. A call to tabLayout.setupWithViewPager(viewPager) finishes the wiring, creating tabs for each entry in the adapter and synchronizing user interactions. Swiping between pages updates the selected tab, and tapping tabs scrolls the pager to the right fragment.
If you need more fine-grained control over user actions, you can attach an OnTabSelectedListener. This listener exposes three callbacks: onTabSelected() when a tab becomes selected, onTabUnselected() when it loses focus, and onTabReselected() when the user taps an already active tab. These hooks are ideal for loading extra data only when a tab is actually viewed or for triggering subtle animations when focus changes.
Scrollable vs fixed tabs and using icons instead of text
Material Design distinguishes clearly between fixed tabs and scrollable tabs, and each has ideal use cases. Fixed tabs are recommended when you have a limited number of short labels that users may want to compare side by side. They are perfect for primary navigation on touch screens, where clarity and stability matter more than fitting a huge catalog of pages.
Scrollable tabs shine when the labels are longer or you need more than about four tabs. If you try to cram long names into a fixed TabLayout, Android will start wrapping labels across multiple lines or even truncating them, which doesn’t just look messy but also harms usability. With scrollable mode enabled, the user can smoothly slide the tab strip left and right, and each label gets enough space to be legible.
Switching between these modes can be done either in XML via app:tabMode or programmatically with setTabMode(). Passing TabLayout.MODE_FIXED yields fixed tabs, while TabLayout.MODE_SCROLLABLE creates a horizontally scrollable list. It’s worth remembering that if you anticipate more than four categories, guidelines strongly suggest leaning towards the scrollable configuration.
Another powerful variation is using icons instead of text for tab labels. By calling getTabAt(index) on a TabLayout instance and then invoking setIcon(), you assign a drawable to a particular tab. This opens up room for very compact tab bars, especially when the icons are universally understood. If you still override getPageTitle() in your adapter, you can combine text and icons; if you omit that override, you end up with icon-only tabs.
Tab behavior is also highly configurable without touching the XML. You can create tabs explicitly with newTab(), rather than relying on setupWithViewPager(), and you can switch the tab mode on the fly, respond to selection via listeners or even insert custom tab views if you need more elaborate designs than a simple label and icon.
Using Android Studio templates to scaffold tabbed activities
Writing a tabbed interface from scratch is great for understanding how everything fits together, but Android Studio can generate a working setup for you in seconds. The IDE ships with templates for common patterns, including a “Tabbed Activity” that’s available in both Java and Kotlin.
When you start a new project, after choosing the application name and target devices, you can pick “Tabbed Activity” from the activity list. In the final configuration dialog there’s an option to select the navigation style, such as “Action Bar Tabs (with ViewPager)”. Once you confirm, Android Studio generates an activity with TabLayout, ViewPager and example fragments wired up so you can run and explore immediately.
These built-in templates are extremely handy for prototypes and straightforward apps. They set up the boilerplate code, example layouts and wiring logic so you can focus your efforts on actual content and specific behavior. In existing projects, you can add the same kind of activity from the File menu by inserting a new “Tabbed Activity” and following the same steps.
For more ambitious apps with complex navigation or a very custom visual language, third-party templates can speed things up further. Marketplaces like Envato provide pre-built Android app templates that include sophisticated tabbed interfaces and Material Design patterns out of the box. These kits are especially useful when you want to concentrate on unique functionality instead of recreating standard navigation from scratch.
Nested tabs in Elementor for advanced web layouts
On the web, page builders like Elementor bring the tabbed interface idea into drag-and-drop design workflows. One particularly flexible pattern is nested tabs: tabs that live inside other tabs, letting you group related content in deeply structured yet compact layouts without overwhelming the page.
A good first step when designing nested tabs is to define a consistent visual language. For instance, a tab might combine a hero image, a title summarizing a venue or plan, a short text description and a button. Once that structure is set, you can vary the actual layout per tab – maybe a single vertical block in one, a two-column arrangement in another, and a three-row composition in a third – while keeping the same elements so the interface still feels cohesive.
Elementor’s nested tabs let you drop in any widget you like, not just plain text. You can embed pricing tables to show monthly, semiannual and annual plans within different tabs, or combine them with loop grids to dynamically filter blog posts, products or portfolio items by category. By aligning categories with tabs, visitors can quickly click through what matters to them without leaving the current page.
Tabs are also a powerful way to guide users through processes or stories. One example is using four or so tabs as steps in a setup flow: each tab might display an icon, a step number and a short label in the tab strip, while the panel content contains unique text and images for that stage. Add motion effects or subtle animations to the images and elements and you can create a guided, narrative experience that’s much more engaging than a long static page.
For more advanced dashboards and admin-style views, designers sometimes go one level deeper and use nested tabs inside nested tabs. Imagine a vertical column of tabs down the left side acting as primary sections, each containing a horizontal set of secondary tabs for sub-views. With a bit of custom CSS – for example, using each tab’s CSS ID to rotate labels and compress the vertical navigation – you can build highly functional tabbed control panels entirely with Elementor containers and loop grids.
The main takeaway is that nested tabs offer almost endless possibilities for structuring content. Whether you are organizing feature tours, pricing options, portfolios or analytics dashboards, the combination of a consistent design language and flexible layouts lets you pack a huge amount of information into a space that still feels intuitive to explore.
Tabbed application mode in Progressive Web Apps
Tabbed interfaces don’t just live inside web pages; they can be baked into the way Progressive Web Apps run as standalone windows. On ChromeOS, a special “tabbed application mode” allows a PWA to present its own document-style tab strip, similar to what you’d expect from a native editor or IDE.
PWAs support several display modes controlled via the display member in the web app manifest. Options include fullscreen, standalone, minimal-ui and browser, and browsers fall back along a defined chain if a particular mode isn’t supported. For even finer control there is a display_override property, which lets developers specify a custom fallback order.
The new tabbed application mode fills a previous gap by providing a built-in tabbed document interface (TDI) for PWAs. Instead of faking tabs inside a page with custom HTML and JavaScript, the app can ask the system to host multiple documents or views in real top-level tabs within a dedicated PWA window. This is different from display: browser, which simply opens the app in a normal browser tab with the full browser UI.
Typical use cases for this mode include productivity apps, communication tools and reading experiences. A code editor PWA might open multiple files in separate tabs, a chat client could provide a tab per room or channel, and a reading app might open article links in new application tabs, all staying neatly within the same window rather than cluttering up the general browser.
There are important differences between this built-in tab mode and custom tab UIs made by developers. System-level tabs can handle large numbers of documents gracefully, benefit from resource isolation and integrate deeply with browser features like navigation history, “Copy link for this page”, casting from the current tab or opening the active document in a regular browser window. If you merely simulate tabs in-page, those capabilities apply to the outer shell, not to each individual sub-view.
How to configure PWA tabbed application mode
Enabling tabbed mode for a PWA starts in the manifest, by setting an appropriate display_override chain. A minimal configuration might specify "display": "standalone" and "display_override": , meaning the browser should prefer a tabbed app window if possible and otherwise fall back to a standard standalone window.
Beyond that, the tab_strip member lets you customize behavior for the app’s tab bar. This object can define two optional subproperties: home_tab and new_tab_button. If you omit tab_strip entirely, the browser will provide default behavior using the app’s start URL as the basis for creating new tabs.
The home tab concept is particularly important. It is a pinned tab that must always be present whenever the app window is open, and it should not navigate away from its defined scope. Any links clicked within this home tab are expected to open in new application tabs instead. You configure it via home_tab.scope_patterns, which is a list of URL patterns (often simple pathnames like "/" or "/index.html") relative to the manifest URL.
The new_tab_button entry describes how the UI’s “new tab” affordance behaves. It has a single url member that specifies which page to open when the user clicks the button, typically something within the app’s scope like "/create". If that URL falls within the home tab’s scope, the app won’t expose a separate “new tab” control at all, since the assumption is that navigation happens from the home view.
An example manifest that wires up a tabbed app window might look like this (conceptually): it defines a name, start_url, display set to standalone, display_override containing "tabbed", a home_tab whose scope covers / and /index.html, and a new_tab_button configured with a "/create" URL. With that setup, users get a persistent home tab plus the ability to open additional documents with a single click.
Apps can also detect whether they are running in tabbed mode at runtime. Using the display-mode media feature, you might write a CSS block like @media (display-mode: tabbed) to fine-tune styles, or use window.matchMedia('(display-mode: tabbed)').matches in JavaScript to check if tabbed application mode is active and adjust UI behavior accordingly.
Finally, there is an interesting interaction with the Launch Handler API. When a tabbed PWA sets "client_mode": "navigate-new" in its launch configuration, app launches can be directed into new tabs within an existing app window instead of spawning multiple windows. This keeps the user’s workspace tidy and reinforces the idea of a single, tab-centric application environment.
Building custom tabbed navigation with PHP, Ajax and Xajax
Long before PWAs had system-level tabs, web developers have been crafting their own tabbed navigation components using HTML, CSS, JavaScript and server-side code. One classic approach uses PHP together with the Xajax framework to load tab content asynchronously and update the page without full reloads.
The HTML structure in such an example is quite straightforward. A wrapper <div> carries a class like pestanas and inside it a <ul> contains <li> elements for each tab. Each list item has a unique ID (such as pestana0, pestana1, pestana2) and a CSS class indicating whether it is active or inactive. Anchor tags inside these list items call a JavaScript function generated by Xajax, for example javascript:void(xajax_cambia_contenido(0)), passing the tab index to the server.
Below the tab list there is a dedicated container for the tab body content, often a <div> with an ID like cuerpopestanas. When the user clicks any tab, the Ajax call fetches the corresponding HTML fragment and injects it into this content area. The page itself never reloads; only the inner HTML of the content container changes.
CSS plays a central role in making the tabs look and feel interactive. Two key classes might be defined: one for inactive tabs (for instance li.pestanainactiva) and another for the selected tab (such as li.pestanaseleccionada). The styling differences – background color, borders, font styles – give the user clear feedback about which tab is active. Additional rules force specific link colors or remove text decoration so that the labels match the desired visual identity.
On the server side, a PHP function like cambia_contenido() orchestrates the response. It receives the tab index as a parameter, builds an xajaxResponse object and looks up the matching content in a PHP array of strings. Then it uses addAssign() to update the page: one call sets the innerHTML of cuerpopestanas to the selected text, another changes the className of the clicked tab to the “selected” style, and a loop resets the remaining tabs to the “inactive” class.
This pattern is flexible regarding where the content comes from. Instead of hard-coded text strings in the array, you could assemble HTML from templates, pull records from a database or compose forms and interactive widgets dynamically. The client side doesn’t care; it just receives updated markup to display in the tab body when the user switches tabs.
Initialization is handled with a small snippet of JavaScript. By calling xajax_cambia_contenido(0) on window.onload, the page automatically selects the first tab and loads its content as soon as the DOM is ready. That way, you don’t have to hard-code any body content in the original HTML – the tab system is fully driven by Ajax from the beginning.
Designing mobile tab bars with best practices
On mobile, the bottom tab bar is one of the most important navigation elements, and it deserves careful attention. A cluttered or inconsistent tab bar can confuse users quickly, especially on small screens where every pixel counts.
One of the first guidelines is to limit the number of items in the tab bar. Aim for four or five icons at most; pushing beyond that tends to shrink touch targets and labels to the point where they are hard to hit accurately and difficult to interpret. If you truly need more navigational options, consider secondary menus or other patterns like drawers.
Icon choice is equally critical. Each icon should clearly convey the main purpose of its section and be immediately recognizable. Text labels can be used sparingly to clarify meaning, but if your icons are well chosen and consistent with platform conventions, users will quickly learn them and rely on the visuals alone.
State indication must be unambiguous. Use color, shape or size changes to highlight the active tab – for instance, a colored accent, a filled icon variant versus an outline, or a subtle size bump. This makes it obvious which section the user is currently viewing. At the same time, it’s generally wise to avoid notification badges or numeric counters directly in the tab bar, as they can create constant visual noise and distract from navigation.
Placement and persistence also matter a lot. The tab bar should sit at the bottom of the screen, consistently visible and reachable by the thumb in both portrait and landscape orientations. Do not hide it behind keyboards, dialogs or floating action buttons, and avoid overlaying it with other components that might intercept taps. Keeping the bar stable builds muscle memory and keeps navigation predictable.
Leveraging Bootstrap, Bootbox.js and Font Awesome for web tab UIs
For traditional web projects, frameworks like Bootstrap 3 offer out-of-the-box tab components that can be styled and extended easily; see how to create a website from scratch. The same toolkit provides buttons, dropdowns, panels and modals, making it simple to assemble cohesive interfaces where tabs fit naturally with the rest of the design.
Bootstrap’s navigation components include ready-made markup and classes for creating horizontal tab strips. By combining the standard nav classes with tab-specific ones, you can switch between content panes with minimal JavaScript. Since all elements share the same Bootstrap styling system, your tabs will automatically align with the look of menus, panels and forms across the site.
To handle alerts and confirmations in a way that matches Bootstrap’s look, many developers lean on Bootbox.js. This small library wraps Bootstrap-styled modals with convenient JavaScript APIs, so you can show confirmation dialogs or alerts when users switch tabs, attempt to close unsaved views or trigger potentially destructive actions – all without breaking the visual consistency established by the CSS framework.
Iconography is often powered by Font Awesome. This extensive icon set integrates smoothly with Bootstrap, giving you a huge catalog of symbols to use inside tab labels or content areas. Whether you need generic icons for home, settings, messages and files or more specialized glyphs, Font Awesome helps communicate meaning without having to design custom images every time.
By combining Bootstrap’s structural components, Bootbox.js modals and Font Awesome icons, you can build rich tabbed interfaces that feel polished and coherent. Tabs, alerts and icons all share the same design language, which makes the user experience feel intentional rather than cobbled together from mismatched pieces.
Across Android, the web, PWAs and PHP-driven sites, tabbed interfaces remain a reliable way to organize multiple views or documents in a single frame. Whether you opt for Material Design’s TabLayout with a ViewPager, nested tabs in Elementor, ChromeOS tabbed application mode for PWAs, or custom solutions using Xajax and Bootstrap, the core goal is the same: keep navigation simple, content discoverable and context changes crystal clear.
