Vize

use-cycle-list

Cycle through a list with wrap-around navigation.

Package @vizejs/composable/use-cycle-list
Own the source vize lib pull composable:use-cycle-list
Runtime exports useCycleList
Gzip budget 768 B

Minimal setup

Use a Vue 3.5+ project with Vue SFC compilation configured (see Vite+ integration).

vp install @vizejs/composable

Import the entry directly. Call lifecycle-bound utilities inside <script setup> so cleanup follows the component scope; consult the runtime contract for this entry's SSR and hydration behavior.

import { useCycleList } from "@vizejs/composable/use-cycle-list";

Try it

Explore component review tips

Cycle through useful review content with wrapping previous/next controls and a direct-choice selector.

Open the interactive example

What to observe: Previous tip from the first item wraps to Handle the empty state; Next tip wraps back to Name the props. Choose Label the controls directly, then continue to the next tip. The title, explanation, selector, and visible position stay synchronized.

A non-empty tuple gives state a defined item type. index is a writable computed ref; v-model.number moves the cursor through it. next() and prev() accept optional step counts, so explicit arrows keep native events out of those arguments. Navigation wraps modulo list length. Reactive list changes retain an existing current item or choose the configured fallback; the scope owns that watcher. This fixed checklist starts synchronously with its first tip during SSR and requires no browser host.

Compare the initial and interacted states
Explore component review tips: initial state
Initial state
Explore component review tips: state after the verified interactions
After the verified interactions

Copy the example

Save this complete SFC in your Vue project. Its public imports and source are the same ones executed in the preview.

<script setup lang="ts">
import { useId } from "vue";
import { useCycleList } from "@vizejs/composable/use-cycle-list";

const tipId = useId();
const {
  state: tip,
  index,
  list,
  next,
  prev,
} = useCycleList([
  { title: "Name the props", text: "Use clear prop names and document the values they accept." },
  {
    title: "Label the controls",
    text: "Give each form control a label that explains its purpose.",
  },
  { title: "Handle the empty state", text: "Explain what users can do when a list has no items." },
] as const);
</script>

<template>
  <div class="composable-example">
    <p>Explore a short component review checklist. Previous and Next wrap around the list.</p>
    <label :for="tipId">Review tip</label>
    <select :id="tipId" v-model.number="index">
      <option v-for="(item, position) in list" :key="item.title" :value="position">
        {{ item.title }}
      </option>
    </select>
    <output aria-live="polite">Tip {{ index + 1 }} of {{ list.length }}: {{ tip.title }}</output>
    <h3>{{ tip.title }}</h3>
    <p>{{ tip.text }}</p>
    <div class="example-actions">
      <button type="button" @click="() => prev()">Previous tip</button>
      <button type="button" @click="() => next()">Next tip</button>
    </div>
    <p>You can also choose a tip directly. Navigation continues from that choice.</p>
  </div>
</template>

Runtime contract

Utility Category Stability SSR Hydration Cleanup Targets Host globals Uses
useCycleList state experimental safe stable reactive-scope web, server, worker, native, desktop, terminal — —

API

useCycleList

Cycle through a list with wrap-around navigation. Non-empty tuple sources (["light", "dark", "system"] as const) yield a state typed as the element union; general arrays may be empty, so their state includes undefined. When the list changes, the cursor keeps the current item if it is still present and otherwise moves to fallbackIndex. Synchronous state: SSR-safe; the list watcher follows the owning scope.

function useCycleList<const List extends readonly [unknown, ...unknown[]]>( list: MaybeRefOrGetter<List>, options?: UseCycleListOptions<List[number]>, ): CycleList<List[number], List[number]>
const { state, next } = useCycleList(["light", "dark", "system"] as const);
next(); // "dark"

useCycleList

function useCycleList<Item>( list: MaybeRefOrGetter<readonly Item[]>, options?: UseCycleListOptions<Item>, ): CycleList<Item | undefined, Item>

useCycleList

function useCycleList<Item>( list: MaybeRefOrGetter<readonly Item[]>, options: UseCycleListOptions<Item> = {}, ): CycleList<Item | undefined, Item>

Types

UseCycleListOptions

Options for useCycleList.

Member Type Description
initialValue? Item Item selected initially.
fallbackIndex? number Index used when the current item is not in the list.
getIndexOf? (item: Item, list: readonly Item[]) => number Locate an item in the list, for example by id.

CycleList

Cursor over a list returned by useCycleList.

Member Type Description
state ShallowRef<Current> Current item. Writable: assigning moves the cursor to that item.
index WritableComputedRef<number> Current index. Writable: assigning wraps around the list.
next (steps?: number) => Current Move forward steps items (default 1), wrapping around.
prev (steps?: number) => Current Move backward steps items (default 1), wrapping around.
go (index: number) => Current Jump to index, wrapping around.
list Readonly<ShallowRef<readonly Item[]>> The list items, for convenience.