Navigation & Toolbars

Floating Navbar

A floating header that hides on downward scroll and reappears on upward scroll.

A compact floating navbar that sits anchored to the top-center of the area it tracks. It reads scroll position with useScroll from motion-v and uses a spring animation to slide up and down. By default it is fixed to the viewport; pass a container to anchor it inside a scrollable element instead.

Usage

Pass a list of links, a brand name, and optionally toggle background blur. The right slot lets you place actions on the opposite side of the nav.

Motion

Floating Navbar

Scroll down to see the navbar hide, scroll up to see it reappear.

<template>
  <div
    ref="box"
    class="relative h-72 w-full max-w-lg overflow-y-auto rounded-2xl border border-default bg-muted/20"
  >
    <MFloatingNavbar
      :container="box"
      position="inline"
      :links="links"
      brand="Motion"
      class="mx-auto mt-4 w-fit"
    >
      <template #right>
        <UButton
          size="xs"
          label="Get Started"
        />
      </template>
    </MFloatingNavbar>

    <div class="space-y-4 p-6">
      <h3 class="text-lg font-semibold">
        Floating Navbar
      </h3>
      <p class="text-muted text-sm">
        Scroll down to see the navbar hide, scroll up to see it reappear.
      </p>
      <div
        v-for="i in 8"
        :key="i"
        class="h-16 rounded-xl bg-default/80 shadow-sm"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const box = ref<HTMLElement | null>(null)

const links = [
  { label: 'Features', to: '#' },
  { label: 'Pricing', to: '#' },
  { label: 'Docs', to: '#' },
]
</script>

Examples

Solid background

With blur disabled the navbar uses an opaque surface instead of a translucent, blurred one.

Motion

Solid background

With blur disabled the navbar uses an opaque surface instead of a translucent, blurred one.

<template>
  <div
    ref="box"
    class="relative h-72 w-full max-w-md overflow-y-auto rounded-2xl border border-default bg-muted/20"
  >
    <MFloatingNavbar
      :container="box"
      position="inline"
      :links="links"
      brand="Motion"
      :blur="false"
      class="mx-auto mt-4 w-fit"
    />

    <div class="space-y-4 p-6">
      <h3 class="text-lg font-semibold">
        Solid background
      </h3>
      <p class="text-muted text-sm">
        With <code>blur</code> disabled the navbar uses an opaque surface instead of a
        translucent, blurred one.
      </p>
      <div
        v-for="i in 8"
        :key="i"
        class="h-16 rounded-xl bg-default/80 shadow-sm"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const box = ref<HTMLElement | null>(null)

const links = [
  { label: 'Features', to: '#' },
  { label: 'Pricing', to: '#' },
  { label: 'Docs', to: '#' },
]
</script>

Custom threshold

scrollThreshold controls how far you must scroll down before the navbar hides. The default is 80px.

Motion

Custom threshold

With :scroll-threshold="240" the navbar stays visible until you have scrolled 240px down, then hides on the next downward scroll.

<template>
  <div
    ref="box"
    class="relative h-72 w-full max-w-md overflow-y-auto rounded-2xl border border-default bg-muted/20"
  >
    <MFloatingNavbar
      :container="box"
      position="inline"
      :links="links"
      brand="Motion"
      :scroll-threshold="240"
      class="mx-auto mt-4 w-fit"
    />

    <div class="space-y-4 p-6">
      <h3 class="text-lg font-semibold">
        Custom threshold
      </h3>
      <p class="text-muted text-sm">
        With <code>:scroll-threshold="240"</code> the navbar stays visible until you
        have scrolled 240px down, then hides on the next downward scroll.
      </p>
      <div
        v-for="i in 8"
        :key="i"
        class="h-16 rounded-xl bg-default/80 shadow-sm"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

const box = ref<HTMLElement | null>(null)

const links = [
  { label: 'Features', to: '#' },
  { label: 'Pricing', to: '#' },
  { label: 'Docs', to: '#' },
]
</script>

Customize

Customize

BrandMotion
Blur
Container
Positioninline
Hide On Scroll
Scroll Threshold80

Props

Prop Type Default Description
linksNavLink[][]Array of navigation links, each with label and to used to render anchor tags in the desktop nav.
brandstring''Text shown on the left side of the navbar.
blurbooleantrueIf true, applies bg-default/70 backdrop-blur-xl; otherwise bg-default.
containerHTMLElementundefinedScroll container to track. When provided, the navbar is sticky inside the container and reacts to its scroll instead of the window's.
position'fixed' | 'inline'fixedPositioning mode. Defaults to inline automatically when a container is passed. fixed pins the navbar to the viewport; inline sticks it to the top of its scroll container.
hideOnScrollbooleantrueWhen false the navbar stays visible regardless of scroll direction.
scrollThresholdnumber80Pixels of downward scroll required before the navbar hides.
classstring''Additional classes merged onto the root motion header.
uiobject-Per-slot classes merged over the theme defaults. See UI.

Slots

Slot Props Description
right-Content rendered on the right side of the navbar, aligned with the brand and links.

UI

This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.floatingNavbar in app.config.ts.

Prop Type Default Description
uiobject-Per-slot classes merged over the theme defaults.
Slot Props Description
root-top-4 z-50 flex items-center gap-6 px-6 py-3 rounded-full border border-default shadow-lg
brand-font-bold text-lg whitespace-nowrap
nav-hidden md:flex items-center gap-1
link-px-3 py-1.5 rounded-full text-sm font-medium hover:bg-muted transition-colors

Accessibility

The navbar is a <motion.header> element with an internal <nav> for the link list. Links are standard anchors. Visibility is animated purely for presentation and the element is never removed from the accessibility tree, so links remain reachable with a keyboard. Consumers should ensure link text is meaningful.

Performance

  • Tracks scrollY from useScroll and reacts only on change via useMotionValueEvent, avoiding unnecessary re-renders.
  • Compares current scroll to the last stored value and hides only after scrolling down past scrollThreshold, reducing flicker near the top.
  • Uses a spring transition on opacity and vertical position (centered via x: '-50%' in fixed mode) for smooth motion without layout thrash.
  • Classes are computed once per prop change; no timers created beyond the scroll subscription managed by motion-v.
Copyright © 2026