@todovue/tv-sidebar
v2.1.1
Published
A versatile sidebar component for TODOvue, offering customizable layouts, themes, and responsive design options.
Maintainers
Readme
TODOvue Sidebar (TvSidebar)
A versatile and flexible Vue 3 sidebar component with multiple display modes: lists, categories (labels), and images. Perfect for blogs, documentation sites, and web applications requiring sidebar navigation or content display. Compatible with both SPA and SSR environments (e.g. Nuxt 3).
Demo: https://tv-sidebar.netlify.app/
Table of Contents
- Features
- Installation
- Quick Start (SPA)
- Nuxt 3 / SSR Usage
- Component Registration Options
- Props
- Events
- Usage Examples
- Data Structure
- Styling
- Navigation Handling
- Accessibility
- SSR Notes
- Development
- Contributing
- License
Features
- Three display modes: List, Categories (labels), and Image
- Event-driven interactions: No built-in navigation; emits click events with full objects
- Item limit: Control how many items to display with the
limitprop - Optional clickable images: Enable with
clickableto emit click events for images - Label/Category support: Display colored category labels with click events
- Responsive design: Adapts to different screen sizes
- SSR compatible: Works seamlessly in Nuxt 3 and SSR contexts
- Customizable styling: Built with SCSS for easy theming
- Tree-shakeable: Vue marked as external dependency
Installation
Using npm:
npm install @todovue/tv-sidebarUsing yarn:
yarn add @todovue/tv-sidebarUsing pnpm:
pnpm add @todovue/tv-sidebarNote: This component depends on
@todovue/tv-labelfor the categories mode.
Quick Start (SPA)
Global registration (main.js / main.ts):
import { createApp } from 'vue'
import App from './App.vue'
import TvSidebar from '@todovue/tv-sidebar'
import '@todovue/tv-sidebar/style.css' // import styles
import '@todovue/tv-label/style.css' // import styles
createApp(App)
.use(TvSidebar) // enables <TvSidebar /> globally
.mount('#app')Local import inside a component:
<script setup>
import { TvSidebar } from '@todovue/tv-sidebar'
import '@todovue/tv-sidebar/style.css' // import styles
import '@todovue/tv-label/style.css' // import styles
const sidebarData = {
title: "Most Popular Blogs",
list: [
{
id: 1,
title: "10 Tips for Creating a Successful YouTube Channel",
link: "/blog/youtube-tips",
},
{
id: 2,
title: "The Benefits of Meditation",
link: "/blog/meditation",
}
]
}
</script>
<template>
<TvSidebar :data="sidebarData" />
</template>Nuxt 3 / SSR Usage
Add the stylesheet to your nuxt.config.ts:
// nuxt.config.ts
export default defineNuxtConfig({
modules: [
'@todovue/tv-card/nuxt'
]
})Create a plugin file: plugins/tv-sidebar.client.ts (or without .client suffix for SSR):
import { defineNuxtPlugin } from '#app'
import TvSidebar from '@todovue/tv-sidebar'
export default defineNuxtPlugin(nuxtApp => {
nuxtApp.vueApp.use(TvSidebar)
})Use anywhere (no router dependency required):
<script setup>
import { TvSidebar } from '@todovue/tv-sidebar'
</script>Component Registration Options
| Approach | When to use |
|---------------------------------------------------------------------|------------------------------------------------|
| Global via app.use(TvSidebar) | Many usages across app / design system install |
| Local named import { TvSidebar } | Isolated / code-split contexts |
| Direct default import import TvSidebar from '@todovue/tv-sidebar' | Single usage or manual registration |
Props
| Prop | Type | Default | Description |
|-----------|---------|---------|------------------------------------------------------------------------------------------------------------------------------------------|
| data | Object | {} | Main data object containing title and content (list, labels, or image). |
| isImage | Boolean | false | Enables image display mode. |
| isLabel | Boolean | false | Enables categories/labels display mode. |
| limit | Number | 0 | Maximum number of items to display (0 = show all). |
| clickable | Boolean | false | When true and isImage, the image becomes interactive and emits a click event with the image object. When false, image is static. |
Events
| Event name (kebab) | Emits (camel) | Description |
|--------------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------|
| clickLabel | clickLabel | Emitted when a label/category is clicked (also emits click with same object). |
| click | click | Emitted when a list item is clicked, when a label is clicked, and when an image is clicked (if clickable). The payload is always the full object. |
Usage:
<TvSidebar
isLabel
:data="categoriesData"
@clickLabel="handleCategoryClick"
@click="handleAnyClick"
/>Usage Examples
Default List Mode
Display a numbered list of items that emit the full object on click:
<script setup>
import { TvSidebar } from '@todovue/tv-sidebar'
const listData = {
title: "Most Popular Blogs",
list: [
{id: 1, title: "10 Tips for Creating a Successful YouTube Channel", link: "/blog/youtube-tips"},
{id: 2, title: "The Benefits of Meditation and How to Get Started", link: "/blog/meditation"},
{id: 3, title: "The Top 5 Destinations for Adventure Travel", link: "/blog/adventure-travel"}
],
}
function handleItemClick(item) {
console.log('Item clicked:', item)
}
</script>
<template>
<TvSidebar :data="listData" @click="handleItemClick" />
</template>Categories (Labels) Mode
Display colored category labels:
<script setup>
import { TvSidebar } from '@todovue/tv-sidebar'
const categoriesData = {
title: "Categories",
labels: [
{
id: 1,
name: "Vue.js",
color: "#4FC08D",
},
{
id: 2,
name: "JavaScript",
color: "#F0DB4F",
},
{
id: 3,
name: "HTML",
color: "#E34F26",
},
{
id: 4,
name: "CSS",
color: "#1572B6",
}
]
}
function handleCategoryClick(category) {
console.log('Category clicked:', category)
// Navigate or filter by category
}
</script>
<template>
<TvSidebar
isLabel
:data="categoriesData"
@clickLabel="handleCategoryClick"
/>
</template>Image Mode
Display an image with title. Make it interactive with clickable if desired:
<script setup>
import { TvSidebar } from '@todovue/tv-sidebar'
const imageData = {
title: "TODOvue Blog",
image: {
src: "https://todovue.com/vue.webp",
alt: "TODOvue Logo",
link: "https://todovue.com/"
},
}
function handleImageClick(image) {
console.log('Image clicked:', image)
}
</script>
<template>
<!-- Non-clickable image -->
<TvSidebar isImage :data="imageData" />
<!-- Clickable image that emits the image object -->
<TvSidebar isImage clickable :data="imageData" @click="handleImageClick" />
</template>With Limit
Limit the number of displayed items:
<template>
<!-- Show only first 5 items -->
<TvSidebar :data="listData" :limit="5" />
<!-- Show only first 8 categories -->
<TvSidebar isLabel :data="categoriesData" :limit="8" />
</template>Nuxt Integration
- Using with Nuxt routing:
- <TvSidebar linkTag="nuxt-link" :data="blogPosts" />Nuxt works without router integration in the component. Handle navigation in your click handlers (see Navigation Handling below).
Data Structure
List Mode Data
{
title: string,
list: Array<{
id: number | string,
title: string,
link?: string // optional; use in your click handler if you want to navigate
}>
}Labels/Categories Mode Data
{
title: string,
labels: Array<{
id: number | string,
name: string,
color: string // Hex color code
}>
}Image Mode Data
{
title: string,
image: {
src: string, // Image URL
alt: string, // Alt text for accessibility
link?: string // optional; use in your click handler for manual navigation
}
}Styling
The component uses SCSS for styling. Styles are automatically included when you import the component. The sidebar includes:
- Clean, minimal design
- Responsive layout
- Title with separator line
- Hover effects on interactive items
- Proper spacing and typography
To customize styles, you can override the CSS classes:
.tv-sidebar-body {
/* Container styles */
}
.tv-sidebar {
/* Main sidebar styles */
}
.tv-sidebar-title h1 {
/* Title styles */
}
.tv-sidebar-content-li {
/* List item styles */
}Navigation Handling
Since the component does not perform navigation, handle it in your click handlers. Example with Vue Router:
<script setup>
import { TvSidebar } from '@todovue/tv-sidebar'
import { useRouter } from 'vue-router'
const router = useRouter()
const listData = {
title: 'Recent Posts',
list: [
{ id: 1, title: 'Getting Started with Nuxt 3', link: '/blog/nuxt-3' },
{ id: 2, title: 'Vue Composition API', link: '/blog/composition-api' }
]
}
function handleClick(item) {
if (item?.link) router.push(item.link)
}
</script>
<template>
<TvSidebar :data="listData" @click="handleClick" />
</template>Accessibility
- Semantic structure with clear headings
- Alt text support for images
- Interactive items emit click events; if you need keyboard accessibility, consider handling
keydown(Enter/Space) on your side or wrapping with accessible elements/roles - Color contrast considerations for labels
SSR Notes
- No direct DOM access (
window/document) → safe for SSR - Compatible with Nuxt 3 out of the box
- Styles are bundled and auto-imported
- No router/nuxt-link dependency inside the component
Development
git clone https://github.com/TODOvue/tv-sidebar.git
cd tv-sidebar
npm install
npm run dev # run demo playground
npm run build # build libraryLocal demo served from Vite using index.html + src/demo examples.
Contributing
PRs and issues welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md.
License
MIT © TODOvue
Dependencies
vue(^3.0.0) - Peer dependency@todovue/tv-label- Used for category/label display mode
Attributions
Crafted with ❤️ for the TODOvue component ecosystem by Cristhian Daza
