npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

navkit-vue

v1.0.41

Published

Vue navigation and utility composables

Readme

NavKit

NavKit is a lightweight, flexible Vue.js navigation library designed for keyboard and TV remote control navigation. It provides an intuitive way to handle spatial navigation in your applications, perfect for smart TV apps, set-top boxes, and keyboard-focused web applications.

Features

  • 🎯 Precise spatial navigation with arrow keys
  • 📺 Perfect for TV applications and remote controls
  • ⌨️ Full keyboard support
  • 🔄 Cyclic navigation option
  • 🔀 Customizable navigation patterns
  • 🎮 Game-like navigation control
  • ↔️ Single-axis navigation support (horizontal or vertical only)
  • ⚡ Lightweight and performant
  • 🔌 Easy to integrate
  • 🎨 Customizable focus styling

Installation

npm install navkit-vue

Basic Usage

The library provides three navigation hooks:

  • useNavigation: Full 2D grid navigation
  • useNavigationX: Horizontal-only navigation
  • useNavigationY: Vertical-only navigation

2D Navigation

import { useNavigation } from "navkit-vue";
<script setup lang="ts">
  import { ref } from "vue";
  import { useNavigation } from "navkit-vue";

  const rows = [3, 3, 3]; // Grid with 3 rows, 3 items each

  const { currentElement, currentRow } = useNavigation({
    rows,
    focusableSelector: "[data-keyboard]", // Custom key - default is [data-focusable]
    onEnter: () => {
      console.log("Enter pressed on:", currentElement.value); //Callback for each keyboard event
    },
  });
</script>

<template>
  <div class="grid">
    <button v-for="item in items" :key="item.id" data-keyboard>
      {{ item.label }}
    </button>
  </div>
</template>

Single-Axis Navigation

For simpler navigation patterns, you can use the dedicated horizontal or vertical navigation hooks:

// Horizontal-only navigation
import { useNavigationX } from "navkit-vue";

const { currentElement } = useNavigationX({
  columns: 5, // Number of items
  focusableSelector: "[data-keyboard]",
});

// Vertical-only navigation
import { useNavigationY } from "navkit-vue";

const { currentElement } = useNavigationY({
  rows: 3, // Number of items
  focusableSelector: "[data-keyboard]",
  cyclic: true,
});

API Reference

useNavigation Options

| Option | Type | Default | Description | | ------------------- | --------------- | -------------------- | ---------------------------------------------- | | rows | Ref<number[]> | Required | Array defining the number of items in each row | | initialPosition | PositionType | { row: 0, col: 0 } | Starting position | | disabled | Ref<boolean> | false | Disables navigation when true | | focusableSelector | string | '[data-focusable]' | CSS selector for focusable elements | | autofocus | boolean | true | Automatically focus first element on mount | | focusClass | string | 'focused' | CSS class applied to focused element | | cyclic | boolean | false | Enable wrapping around edges | | invertAxis | boolean | false | Swap vertical/horizontal navigation | | autoNextRow | boolean | false | Auto-advance to next row | | holdColumnPerRow | boolean | false | Maintain column position when changing rows |

useNavigationX / Y Options

| Option | Type | Default | Description | | ------------------- | -------------- | -------------------- | ------------------------------------------ | | columns/rows | Ref<number> | Required | Number of navigable items | | initialPosition | number | 0 | Starting position | | disabled | Ref<boolean> | false | Disables navigation when true | | focusableSelector | string | '[data-focusable]' | CSS selector for focusable elements | | autofocus | boolean | true | Automatically focus first element on mount | | focusClass | string | 'focused' | CSS class applied to focused element | | cyclic | boolean | false | Enable wrapping around edges | | onRowStart | Callback Fn | null | Callback Fn on Row Start | | onRowEnd | Callback Fn | null | Callback Fn on Row End | | onEnter | Callback Fn | null | Callback Fn on Enter | | onReturn | Callback Fn | null | Callback Fn on Back |

Callback Events

| Event | Parameters | Description | | --------------- | ---------------------------------- | ------------------------------------------- | | onColumnStart | () => void | Called when navigation reaches first column | | onColumnEnd | () => void | Called when navigation reaches last column | | onRowStart | () => void | Called when navigation reaches first row | | onRowEnd | () => void | Called when navigation reaches last row | | onEnter | (position: PositionType) => void | Called when Enter key is pressed | | onReturn | (position: PositionType) => void | Called when Return/Back key is pressed | | onDown | () => void | For X Navigation Type | | onUp | () => void | For X Navigation Type | | onLeft | () => void | For Y Navigation Type | | onRight | () => void | For Y Navigation Type |

Return Values

| Value | Type | Description | | ----------------- | ----------------------------------------- | ------------------------------ | | position | Ref<PositionType> | Current focus position | | currentRow | number | Current row index | | currentElement | Ref<HTMLElement \| null> | Currently focused element | | isDisabled | Ref<boolean> | Current disabled state | | setRowPosition | (row: number, position: number) => void | Set position for specific row | | toggleDisabled | (value?: boolean) => void | Toggle or set disabled state | | isValidPosition | (position: PositionType) => boolean | Check if position is valid | | focusElement | (element: HTMLElement \| null) => void | Programmatically focus element |

Styling

NavKit uses a class-based approach for styling focused elements. By default, it applies the focused class to the currently focused element. You can customize this by:

.focused {
  outline: 2px solid #007bff;
  box-shadow: 0 0 10px rgba(0, 123, 255, 0.5);
}

Advanced Usage

Custom Grid Layout

<script setup lang="ts">
  const rows = computed(() => [
    firstRow.value.length,
    secondRow.value.length,
    thirdRow.value.length,
  ]); // Irregular grid layout

  const { currentElement } = useNavigation({
    rows,
    cyclic: true, // Enable wrap-around navigation
    autoNextRow: true, // Auto-advance to next row
    focusableSelector: "[data-selector]", // This is optinal, default is `[data-focusable]`
    onEnter: (position) => {
      console.log("Selected position:", position);
    },
  });
</script>

Single-Axis Navigation Example

<script setup lang="ts">
  import { ref } from "vue";
  import { useNavigationX } from "navkit";

  const columns = computed(() => columns.value.length);

  const { currentElement } = useNavigationX({
    columns,
    cyclic: true,
    onEnter: () => {
      console.log("Selected:", currentElement.value?.textContent);
    },
  });
</script>

<template>
  <div class="horizontal-menu">
    <button v-for="item in menuItems" :key="item.id" data-keyboard>
      {{ item.label }}
    </button>
  </div>
</template>

TV Remote Navigation

<script setup lang="ts">
  const { currentElement, toggleDisabled } = useNavigation({
    rows: ref([3, 3, 3]),
    focusableSelector: "[data-keyboard]",
    focusClass: "tv-focused",
    onReturn: () => {
      // Handle back button
      router.back();
    },
  });
</script>

Scroll Into Focus

The useScrollIntoFocus hook provides automatic scrolling functionality to ensure focused elements are always visible within their container. This is particularly useful for large lists or grids where content may extend beyond the viewport.

import { useScrollIntoFocus } from "navkit-vue";

const { currentElement, position } = useNavigation({
  rows: [5, 5, 5],
});

useScrollIntoFocus({
  position,
  selectedElement: currentElement,
  behavior: "smooth",
  parentSelector: "[data-parent]",
  buffer: 180,
});

useScrollIntoFocus Options

| Option | Type | Default | Description | | ----------------- | -------------------------- | ----------------- | ------------------------------------------------------- | | position | Ref<PositionType> | Required | Current navigation position | | selectedElement | Ref<HTMLElement \| null> | Required | Currently focused element | | behavior | "smooth" \| "auto" | "smooth" | Scroll behavior | | delay | number | 1000 | Delay in milliseconds for scroll throttling/debouncing | | parentSelector | string | "[data-parent]" | CSS selector for scrollable container | | buffer | number | 180 | Default buffer space around focused element (in pixels) | | bufferX | number | buffer | Horizontal buffer space (overrides default buffer) | | bufferY | number | buffer | Vertical buffer space (overrides default buffer) | | suppressLogs | boolean | true | Suppress warning logs | | scrollType | "throttle" \| "debounce" | "throttle" | Scroll event handling type |

Browser Support

NavKit supports all modern browsers and smart TV platforms that support Vue.js.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.