Skip to main content

AppNav

Open in ChatGPT
Shadow dwc-app-nav dwc-app-nav-item dwc-app-nav-label 24.12
Java API

The AppNav component creates a side navigation menu out of AppNavItem entries. Items can link to internal views or external resources, nest under parent items to form hierarchical menus, and carry icons, badges, or other components to give users more context at a glance.

Adding and nesting items​

AppNavItem instances are used to populate the AppNav structure. These items can be simple links or nested group headers that contain child items. Group headers without links act as expandable containers.

Use addItem() to include items in the nav:

AppNavItem dashboard = new AppNavItem("Dashboard", "/dashboard");
AppNavItem admin = new AppNavItem("Admin");
admin.addItem(new AppNavItem("Users", "/admin/users"));
admin.addItem(new AppNavItem("Settings", "/admin/settings"));

AppNav nav = new AppNav();
nav.addItem(dashboard);
nav.addItem(admin);
Linking Group Items

Top-level items in a navigation tree are typically meant to be expandable—not clickable links. Setting a path on such items can confuse users who expect them to reveal sub-items instead of navigating elsewhere.

If you want the group header to trigger a custom action (such as opening external docs), keep the group path empty and instead add an interactive control like an IconButton to the item's suffix. This keeps the UX consistent and clean.

Show Code

Linking Items​

Each AppNavItem can navigate to an internal view or an external link. You can define this using static paths or registered view classes.

Static paths​

Use string paths to define links directly:

AppNavItem docs = new AppNavItem("Docs", "/docs");
AppNavItem help = new AppNavItem("Help", "https://support.example.com");

Registered views​

If your views are registered with the router, you can pass the class instead of a hardcoded URL:

AppNavItem settings = new AppNavItem("Settings", SettingsView.class);

If your annotated route supports route parameters, you can also pass a ParametersBag:

ParametersBag params = ParametersBag.of("id=123");
AppNavItem advanced = new AppNavItem("User", UserView.class, params);

With query parameters​

Pass a ParametersBag to include query strings:

ParametersBag params = ParametersBag.of("param1=value1&param2=value2");
AppNavItem advanced = new AppNavItem("Advanced", SettingsView.class, params);
advanced.setQueryParameters(params);

Target behavior​

Control how links open using setTarget(). This is especially useful for external links or pop-out views.

  • SELF (default): Opens in the current view.
  • BLANK: Opens in a new tab or window.
  • PARENT: Opens in the parent browsing context.
  • TOP: Opens in the top-level browsing context.
AppNavItem help = new AppNavItem("Help", "https://support.example.com");
help.setTarget(AppNavItem.NavigationTarget.BLANK);

Prefix and suffix​

AppNavItem supports prefix and suffix components. Use these to provide visual clarity with icons, badges, or buttons.

  • Prefix: appears before the label, useful for icons.
  • Suffix: appears after the label, great for badges or actions.
AppNavItem notifications = new AppNavItem("Alerts");
notifications.setPrefixComponent(TablerIcon.create("alert"));
notifications.setSuffixComponent(TablerIcon.create("link"));

Auto-opening groups​

Use setAutoOpen(true) on the AppNav component to automatically expand nested groups when the app is refreshed.

nav.setAutoOpen(true);

Section labels 26.02​

AppNavLabel is a non-interactive heading that titles a run of items. A label applies to every item that follows it, up to the next label or the end of the menu, which lets a long list of top-level items read as a few named groups without nesting them.

Labels are added with add() rather than addItem(), and the order of the calls defines the sections:

AppNav nav = new AppNav();
nav.addItem(new AppNavItem("Dashboard", DashboardView.class, TablerIcon.create("layout-dashboard")));

nav.add(new AppNavLabel("Analytics"));
nav.addItem(new AppNavItem("Overview", OverviewView.class));
nav.addItem(new AppNavItem("Reports", ReportsView.class));

nav.add(new AppNavLabel("Other"));
nav.addItem(new AppNavItem("Settings", SettingsView.class));

The nav hides a label automatically when its section has no visible items, so a label disappears when a search filters its items out or when all of them are pinned to the top of the menu.

Label prefix and suffix​

Like AppNavItem, a label supports prefix, and suffix components. Pass a prefix to the constructor, or set either one afterward:

AppNavLabel analytics = new AppNavLabel("Analytics", TablerIcon.create("chart-pie"));
analytics.setSuffixComponent(new Badge().setText("2").setTheme(BadgeTheme.WARNING));

nav.add(analytics);

The example below groups a menu under three labels, the first of which carries an Icon prefix and a Badge suffix. Dashboard sits above the first label, so it belongs to no section.

Show Code

Pinning 26.01​

Pinning lets a user lift the items they reach for most into a group at the top of the navigation, so a deep menu still keeps a short list of favorites within one click. It's off by default. Turn it on through the pinning configuration:

AppNav nav = new AppNav();
nav.getPinning().setEnabled(true);

Once enabled, every navigable leaf item shows a pin toggle. The toggle is revealed on hover and on keyboard focus, so it stays reachable without a mouse. Activating it moves the item into the pinned group at the top of the nav.

A few rules govern what can be pinned and how the group behaves:

  • Only navigable leaf items are pinnable. Group headers (items with children) are never pinnable.
  • The pinned group appears only once something is pinned, and disappears again when the last item is unpinned.
  • Unpinning returns an item to its exact original position, including items nested several levels deep inside groups.
  • The item is moved, not copied, so any prefix or suffix content and any listeners attached to it keep working while it sits in the pinned group.

The demo below has pinning enabled with a custom group title and Dashboard pinned on load. Hover or focus a leaf item to reveal its pin toggle.

Show Code

Starting an item pinned​

Start an item in the pinned group by setting its pinned state. Use isPinned() to read the current state.

AppNavItem reports = new AppNavItem("Reports", "/reports");
reports.setPinned(true);
Pinning must be enabled

setPinned(true) only takes effect when pinning is enabled on the AppNav through getPinning().setEnabled(true). Without it, the call has no effect.

Pinned group title​

The pinned group is labeled Pinned by default. Change it to fit your app:

nav.getPinning().setTitle("Favorites");

Pin keys​

Each pinnable item carries a key that identifies it for persistence and for the pin event. When you don't set one, the key falls back to the item's path, so getPinKey() always returns a usable value.

AppNavItem reports = new AppNavItem("Reports", "/reports");
reports.setPinKey("reports");

Set an explicit key when the path can change at runtime. A stable key keeps a pin matched to the right item across reloads even if its URL moves.

Autosave to local storage​

Pins live only for the current page view unless you persist them. Autosave is the simplest option: it stores the set of pinned items in the browser's local storage and restores them on reload. It's off by default. It needs a stable id (or name) on the component for the storage key, and the AppNav(String id) constructor is the convenient way to set one:

AppNav nav = new AppNav("main-nav"); // gives autosave a stable storage key
nav.getPinning().setAutosave(true);
Autosave needs an id

With no id (or name) on the component, autosave silently does nothing, since it has no stable key to store under. Persistence is per browser, so pins don't follow a user to another device or browser.

Custom persistence​

For persistence you control, for example per user on the server, turn autosave off and drive it yourself through the pin event and setPinned:

nav.getPinning().setAutosave(false);

// persist the current set of pinned keys whenever it changes
nav.onPin(event -> savePins(event.getKeys()));

// on load, restore each saved key
restoredKeys.forEach(key -> findItem(key).setPinned(true));

Reacting to pin changes​

The pin event fires whenever an item is pinned or unpinned. It carries the item that changed, its key, the new pinned state, and the full ordered set of pinned keys:

nav.onPin(event -> {
AppNavItem item = event.getItem(); // the item that changed, or null if it is no longer in the nav
boolean pinned = event.isPinned();
String key = event.getKey();
List<String> all = event.getKeys(); // every pinned key, in pinned order
});

getItem() resolves the item by matching its pin key, and returns null when the item is no longer part of the navigation.

Pin icons​

The toggle uses the built in dwc:pin icon while an item is unpinned and dwc:pinned-off while it's pinned. Swap in your own through setUnpinnedIcon and setPinnedIcon, which accept any IconDefinition:

nav.getPinning()
.setUnpinnedIcon(TablerIcon.create("pin"))
.setPinnedIcon(TablerIcon.create("pinned-off"));

Pin toggle on touchscreens​

Touchscreens have no hover to reveal the pin, so the toggle is hidden there by default. Keep it visible and tappable on touchscreens with setTouchVisible(true):

nav.getPinning().setTouchVisible(true);

The search field filters the menu by item label as the user types. It's off by default. You can show it and give it a placeholder through the search configuration:

nav.getSearch().setFieldVisible(true);
nav.getSearch().setPlaceholder("Search");

As the user types, the nav filters items by label, opens any group that contains a match, and shows an empty message when nothing matches. Pinned shortcuts stay visible while searching, so a user's favorites remain one click away even mid filter.

Show Code

Empty message​

Set the message shown when a search returns no results. Plain text is rendered as text:

nav.getSearch().setEmptyMessage("No items found");

Hide the built in field and feed the filter from an input of your own. Push the current term in through setTerm:

nav.getSearch().setFieldVisible(false);

myField.onModify(event -> nav.getSearch().setTerm(event.getText()));

To react to what the user types in the built in field, listen for the search event:

nav.onSearch(event -> log(event.getTerm()));

Styling AppNavItem​

Loading...