vue3-router-tab 1.2.7 → 1.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  A Vue 3 tab-bar plugin that keeps multiple routes alive with transition support, context menus, and optional cookie-based persistence.
4
4
 
5
+ ## ✨ Features
6
+
7
+ - 🎯 **Multi-tab Navigation** - Keep multiple routes alive simultaneously
8
+ - 🔄 **7 Built-in Transitions** - Smooth page transitions (swap, slide, fade, scale, flip, rotate, bounce)
9
+ - 🎨 **Reactive Tab Titles** - Automatically update tab titles, icons, and closability from component state
10
+ - 🖱️ **Context Menu** - Right-click tabs for refresh, close, and navigation options
11
+ - 🔀 **Drag & Drop** - Reorder tabs with drag-and-drop
12
+ - 💾 **Cookie Persistence** - Restore tabs on page refresh
13
+ - 🎭 **Theme Support** - Light, dark, and system themes with customizable colors
14
+ - ⚡ **KeepAlive Support** - Preserve component state when switching tabs
15
+ - 🎛️ **Highly Configurable** - Extensive props and events for customization
16
+ - 📱 **TypeScript Support** - Full TypeScript definitions included
17
+
5
18
  ## Installation
6
19
 
7
20
  ```bash
@@ -121,136 +134,315 @@ const routes = [
121
134
  | `persistence` | `object \| null` | `null` | Persistence configuration |
122
135
  | `sortable` | `boolean` | `true` | Enable drag-and-drop tab sorting |
123
136
 
124
- ### Reactive Tab Updates
137
+ ## Page Transitions
138
+
139
+ Vue3 Router Tab includes 7 built-in page transition effects that are displayed when switching between tabs or refreshing pages.
140
+
141
+ ### Available Transitions
125
142
 
126
- RouterTab automatically watches for reactive properties in your page components and updates the corresponding tab information in real-time. The tab titles, icons, and other properties displayed in the tab bar will automatically update when the reactive properties in your components change.
143
+ | Transition | Description | Use Case |
144
+ |------------|-------------|----------|
145
+ | `router-tab-swap` | Slides up/down with fade (default) | General purpose, smooth |
146
+ | `router-tab-slide` | Horizontal sliding animation | Dashboard-style navigation |
147
+ | `router-tab-fade` | Simple opacity fade | Subtle, minimal distraction |
148
+ | `router-tab-scale` | Zoom in/out effect | Dramatic, attention-grabbing |
149
+ | `router-tab-flip` | 3D flip animation | Creative, modern feel |
150
+ | `router-tab-rotate` | Rotation with scale | Playful, dynamic |
151
+ | `router-tab-bounce` | Elastic bounce effect | Fun, energetic |
127
152
 
128
- #### Watched Properties
153
+ ### Using Transitions
129
154
 
130
- The following reactive properties in your page components are automatically monitored:
155
+ #### Default Transition
131
156
 
132
- - **`routeTabTitle`** - Updates the tab title
133
- - **`routeTabIcon`** - Updates the tab icon
134
- - **`routeTabClosable`** - Updates whether the tab can be closed
135
- - **`routeTabMeta`** - Updates additional tab metadata
157
+ ```vue
158
+ <router-tab />
159
+ <!-- Uses router-tab-swap by default -->
160
+ ```
136
161
 
137
- #### Basic Usage
162
+ #### Custom Transition
163
+
164
+ ```vue
165
+ <router-tab
166
+ :page-transition="{ name: 'router-tab-scale', mode: 'out-in' }"
167
+ />
168
+ ```
169
+
170
+ #### String Shorthand
171
+
172
+ ```vue
173
+ <router-tab page-transition="router-tab-fade" />
174
+ ```
175
+
176
+ #### Dynamic Transitions
177
+
178
+ Change transitions at runtime:
138
179
 
139
180
  ```vue
140
181
  <template>
141
182
  <div>
142
- <button @click="updateTitle">Update Title</button>
143
- <button @click="toggleLoading">Toggle Loading</button>
183
+ <select v-model="currentTransition">
184
+ <option value="router-tab-swap">Swap</option>
185
+ <option value="router-tab-slide">Slide</option>
186
+ <option value="router-tab-fade">Fade</option>
187
+ <option value="router-tab-scale">Scale</option>
188
+ <option value="router-tab-flip">Flip</option>
189
+ <option value="router-tab-rotate">Rotate</option>
190
+ <option value="router-tab-bounce">Bounce</option>
191
+ </select>
192
+
193
+ <router-tab
194
+ :page-transition="{ name: currentTransition, mode: 'out-in' }"
195
+ />
144
196
  </div>
145
197
  </template>
146
198
 
147
199
  <script setup>
148
- import { ref, computed } from 'vue'
200
+ import { ref } from 'vue'
201
+ const currentTransition = ref('router-tab-swap')
202
+ </script>
203
+ ```
149
204
 
150
- // Simple reactive title - tab updates immediately when this changes
151
- const routeTabTitle = ref('My Page')
205
+ ### Custom Transitions
152
206
 
153
- // Dynamic title based on state
154
- const isLoading = ref(false)
155
- const routeTabTitle = computed(() =>
156
- isLoading.value ? 'Loading...' : 'My Page'
157
- )
207
+ Create your own transitions by defining CSS classes:
158
208
 
159
- // Dynamic icon - tab icon updates automatically
160
- const routeTabIcon = computed(() =>
161
- isLoading.value ? 'mdi-loading mdi-spin' : 'mdi-page'
162
- )
209
+ ```css
210
+ /* Your custom transition */
211
+ .my-custom-enter-active,
212
+ .my-custom-leave-active {
213
+ transition: all 0.5s ease;
214
+ }
163
215
 
164
- // Conditional closability - tab close button appears/disappears automatically
165
- const routeTabClosable = computed(() => !isLoading.value)
216
+ .my-custom-enter-from {
217
+ opacity: 0;
218
+ transform: translateX(100px);
219
+ }
166
220
 
167
- // Functions that trigger reactive updates
168
- function updateTitle() {
169
- routeTabTitle.value = `Updated ${Date.now()}`
221
+ .my-custom-leave-to {
222
+ opacity: 0;
223
+ transform: translateX(-100px);
170
224
  }
225
+ ```
226
+
227
+ ```vue
228
+ <router-tab page-transition="my-custom" />
229
+ ```
171
230
 
172
- function toggleLoading() {
173
- isLoading.value = !isLoading.value
231
+ ### Transition Tips
232
+
233
+ - **Performance**: Use `mode: 'out-in'` for smooth transitions without layout shifts
234
+ - **Duration**: Built-in transitions are optimized at 0.5-0.8 seconds
235
+ - **Accessibility**: Consider users with motion sensitivity - provide options to disable
236
+ - **Context**: Match transition style to your app's design language
237
+
238
+ ## Changing Tab Titles Dynamically
239
+
240
+ Vue3 Router Tab automatically watches for reactive properties in your page components and updates the corresponding tab information in real-time.
241
+
242
+ ### Quick Start
243
+
244
+ Simply expose reactive properties from your component:
245
+
246
+ ```vue
247
+ <template>
248
+ <div>
249
+ <h1>{{ routeTabTitle }}</h1>
250
+ <button @click="updateTitle">Change Tab Title</button>
251
+ </div>
252
+ </template>
253
+
254
+ <script setup>
255
+ import { ref } from 'vue'
256
+
257
+ // This ref is automatically watched - tab title updates when it changes!
258
+ const routeTabTitle = ref('My Page')
259
+
260
+ function updateTitle() {
261
+ routeTabTitle.value = 'Updated Title'
174
262
  }
175
263
  </script>
176
264
  ```
177
265
 
178
- #### Advanced Examples
266
+ ### Watched Properties
267
+
268
+ The following reactive properties are automatically monitored:
269
+
270
+ | Property | Description | Example |
271
+ |----------|-------------|---------|
272
+ | `routeTabTitle` | Tab title text | `ref('Dashboard')` |
273
+ | `routeTabIcon` | Tab icon class | `ref('mdi-home')` |
274
+ | `routeTabClosable` | Can tab be closed | `ref(true)` |
275
+ | `routeTabMeta` | Additional metadata | `ref({ badge: 5 })` |
276
+
277
+ ### Dynamic Titles with Computed
278
+
279
+ Create titles that update based on your component state:
179
280
 
180
281
  ```vue
181
282
  <script setup>
182
283
  import { ref, computed } from 'vue'
183
284
 
285
+ const isLoading = ref(false)
184
286
  const notifications = ref(0)
185
- const hasError = ref(false)
186
- const isProcessing = ref(false)
187
287
 
188
- // Dynamic title with notification count
288
+ // Tab title automatically updates when dependencies change
189
289
  const routeTabTitle = computed(() => {
190
- if (hasError.value) return 'Error!'
191
- if (isProcessing.value) return 'Processing...'
290
+ if (isLoading.value) return 'Loading...'
192
291
  if (notifications.value > 0) return `Messages (${notifications.value})`
193
292
  return 'Dashboard'
194
293
  })
195
294
 
196
- // State-based icons
197
- const routeTabIcon = computed(() => {
198
- if (hasError.value) return 'mdi-alert'
199
- if (isProcessing.value) return 'mdi-loading mdi-spin'
200
- if (notifications.value > 0) return 'mdi-bell-badge'
201
- return 'mdi-view-dashboard'
295
+ // Tab icon changes based on state
296
+ const routeTabIcon = computed(() =>
297
+ isLoading.value ? 'mdi-loading mdi-spin' : 'mdi-view-dashboard'
298
+ )
299
+
300
+ // Prevent closing during operations
301
+ const routeTabClosable = computed(() => !isLoading.value)
302
+ </script>
303
+ ```
304
+
305
+ ### Real-World Examples
306
+
307
+ #### Example 1: User Profile with Name
308
+
309
+ ```vue
310
+ <script setup>
311
+ import { ref, computed, onMounted } from 'vue'
312
+
313
+ const user = ref(null)
314
+ const isLoading = ref(true)
315
+
316
+ const routeTabTitle = computed(() =>
317
+ isLoading.value ? 'Loading...' : `Profile - ${user.value?.name || 'Unknown'}`
318
+ )
319
+
320
+ const routeTabIcon = computed(() =>
321
+ isLoading.value ? 'mdi-loading mdi-spin' : 'mdi-account'
322
+ )
323
+
324
+ onMounted(async () => {
325
+ user.value = await fetchUser()
326
+ isLoading.value = false
202
327
  })
328
+ </script>
329
+ ```
330
+
331
+ #### Example 2: Form with Unsaved Changes
332
+
333
+ ```vue
334
+ <script setup>
335
+ import { ref, computed } from 'vue'
203
336
 
204
- // Prevent closing during critical operations
205
- const routeTabClosable = computed(() => !isProcessing.value)
337
+ const formData = ref({})
338
+ const hasUnsavedChanges = ref(false)
339
+
340
+ const routeTabTitle = computed(() =>
341
+ hasUnsavedChanges.value ? '• Edit Form' : 'Edit Form'
342
+ )
343
+
344
+ const routeTabClosable = computed(() => !hasUnsavedChanges.value)
345
+
346
+ function onChange() {
347
+ hasUnsavedChanges.value = true
348
+ }
349
+ </script>
350
+ ```
351
+
352
+ #### Example 3: Real-time Notifications
353
+
354
+ ```vue
355
+ <script setup>
356
+ import { ref, computed, onMounted } from 'vue'
357
+
358
+ const unreadCount = ref(0)
359
+
360
+ const routeTabTitle = computed(() => {
361
+ if (unreadCount.value === 0) return 'Messages'
362
+ return `Messages (${unreadCount.value})`
363
+ })
364
+
365
+ const routeTabIcon = computed(() =>
366
+ unreadCount.value > 0 ? 'mdi-bell-badge' : 'mdi-bell-outline'
367
+ )
206
368
 
207
- // Custom metadata for advanced use cases
208
- const routeTabMeta = computed(() => ({
209
- status: hasError.value ? 'error' : 'normal',
210
- count: notifications.value,
211
- timestamp: Date.now()
212
- }))
369
+ // Simulate real-time updates
370
+ onMounted(() => {
371
+ setInterval(() => {
372
+ unreadCount.value = Math.floor(Math.random() * 10)
373
+ }, 5000)
374
+ })
213
375
  </script>
214
376
  ```
215
377
 
216
- #### Using Composables
378
+ ### Using the Composable API
217
379
 
218
- For easier reactive tab management, use the provided composables:
380
+ For advanced use cases, use the `useReactiveTab` composable:
219
381
 
220
382
  ```vue
221
383
  <script setup>
222
- import {
223
- useReactiveTab,
224
- useLoadingTab,
225
- useNotificationTab,
226
- useStatusTab
227
- } from 'vue3-router-tab'
228
-
229
- // Basic composable
230
- const { routeTabTitle, routeTabIcon, updateTitle } = useReactiveTab({
231
- title: 'My Page',
232
- icon: 'mdi-page'
384
+ import { useReactiveTab } from 'vue3-router-tab'
385
+ import { ref } from 'vue'
386
+
387
+ const user = ref({ name: 'John Doe', status: 'online' })
388
+
389
+ const {
390
+ routeTabTitle,
391
+ routeTabIcon,
392
+ routeTabClosable
393
+ } = useReactiveTab({
394
+ title: () => `${user.value.name} - ${user.value.status}`,
395
+ icon: () => user.value.status === 'online' ? 'mdi-account' : 'mdi-account-off',
396
+ closable: () => user.value.status !== 'editing'
233
397
  })
398
+ </script>
399
+ ```
234
400
 
235
- // Loading state preset
236
- const isLoading = ref(false)
237
- const loadingTab = useLoadingTab(isLoading, 'Dashboard')
401
+ ### Important Notes
238
402
 
239
- // Notification preset
240
- const count = ref(0)
241
- const notificationTab = useNotificationTab(count, 'Messages')
403
+ 1. **Automatic Exposure in `<script setup>`**: Properties defined in `<script setup>` are automatically exposed - no need for `defineExpose()`
404
+ 2. **Reactive types**: Use `ref()` or `computed()` - plain values won't trigger updates
405
+ 3. **Automatic watching**: No manual watchers needed - the plugin handles everything
406
+ 4. **Performance**: Only active tab components are watched to minimize overhead
242
407
 
243
- // Status preset
244
- const status = ref('normal')
245
- const statusTab = useStatusTab(status, 'Process')
408
+ > 💡 **Try it yourself**: Check out the live demo at `/title-test` in the example app to see all these features in action!
409
+
410
+ ### Options API Support
411
+
412
+ If you're using the Options API, you need to expose the properties:
413
+
414
+ ```vue
415
+ <script>
416
+ import { ref, computed } from 'vue'
417
+
418
+ export default {
419
+ setup() {
420
+ const routeTabTitle = ref('My Page')
421
+ const routeTabIcon = computed(() => 'mdi-page')
422
+
423
+ return {
424
+ routeTabTitle,
425
+ routeTabIcon
426
+ }
427
+ }
428
+ }
246
429
  </script>
247
430
  ```
248
431
 
249
- **Available Composables:**
250
- - `useReactiveTab(config)` - Basic reactive tab management
251
- - `useLoadingTab(loading, baseTitle)` - Loading state management
252
- - `useNotificationTab(count, baseTitle, baseIcon)` - Notification badges
253
- - `useStatusTab(status, baseTitle)` - Status-based updates
432
+ Or with `defineExpose`:
433
+
434
+ ```vue
435
+ <script setup>
436
+ import { ref } from 'vue'
437
+
438
+ const routeTabTitle = ref('My Page')
439
+
440
+ // Only needed if you're NOT using top-level refs in <script setup>
441
+ defineExpose({ routeTabTitle })
442
+ </script>
443
+ ```
444
+
445
+ **Note**: With `<script setup>`, top-level bindings are automatically exposed, so `defineExpose` is typically not needed.
254
446
 
255
447
  ### Tab Closing Behavior
256
448
 
@@ -394,6 +586,141 @@ Pass `false` to disable the context menu entirely.
394
586
  - `closeRights` - Close tabs to the right
395
587
  - `closeOthers` - Close all other tabs
396
588
 
589
+ ## Programmatic API
590
+
591
+ Access the router tabs controller to programmatically manage tabs.
592
+
593
+ ### Using the Composable
594
+
595
+ ```vue
596
+ <script setup>
597
+ import { useRouterTabs } from 'vue3-router-tab'
598
+
599
+ const tabs = useRouterTabs()
600
+
601
+ // Available methods
602
+ tabs.openTab('/users') // Open a tab
603
+ tabs.closeTab(tabId) // Close a specific tab
604
+ tabs.refreshTab(tabId) // Refresh a tab
605
+ tabs.refreshAll() // Refresh all tabs
606
+ tabs.closeAll() // Close all tabs
607
+ tabs.closeOthers(tabId) // Close all except specified tab
608
+ </script>
609
+ ```
610
+
611
+ ### Using Global Property
612
+
613
+ ```vue
614
+ <script setup>
615
+ import { getCurrentInstance } from 'vue'
616
+
617
+ const instance = getCurrentInstance()
618
+ const tabs = instance?.appContext.config.globalProperties.$tabs
619
+
620
+ // Same methods available
621
+ tabs?.openTab('/dashboard')
622
+ tabs?.refreshTab('users-123')
623
+ </script>
624
+ ```
625
+
626
+ ### Controller Methods
627
+
628
+ | Method | Parameters | Description |
629
+ |--------|------------|-------------|
630
+ | `openTab(to, active?, replace?)` | Route location, activate flag, replace flag | Open or activate a tab |
631
+ | `closeTab(id, options?)` | Tab ID, close options | Close a specific tab |
632
+ | `refreshTab(id, force?)` | Tab ID, force flag | Refresh tab component |
633
+ | `refreshAll(force?)` | Force flag | Refresh all tabs |
634
+ | `closeAll(options?)` | Close options | Close all closable tabs |
635
+ | `closeOthers(id, options?)` | Tab ID, options | Close all tabs except specified |
636
+ | `removeTab(id, options?)` | Tab ID, options | Remove tab without navigation |
637
+
638
+ ### Example: Custom Tab Controls
639
+
640
+ ```vue
641
+ <template>
642
+ <div>
643
+ <button @click="openDashboard">Open Dashboard</button>
644
+ <button @click="refreshCurrent">Refresh Current</button>
645
+ <button @click="closeAllTabs">Close All</button>
646
+ </div>
647
+ </template>
648
+
649
+ <script setup>
650
+ import { useRouterTabs } from 'vue3-router-tab'
651
+ import { useRoute } from 'vue-router'
652
+
653
+ const tabs = useRouterTabs()
654
+ const route = useRoute()
655
+
656
+ function openDashboard() {
657
+ tabs.openTab('/dashboard', true)
658
+ }
659
+
660
+ function refreshCurrent() {
661
+ const currentTab = tabs.tabs.find(t => t.to.path === route.path)
662
+ if (currentTab) {
663
+ tabs.refreshTab(currentTab.id, true)
664
+ }
665
+ }
666
+
667
+ function closeAllTabs() {
668
+ tabs.closeAll({ force: true })
669
+ }
670
+ </script>
671
+ ```
672
+
673
+ ### Tab State Access
674
+
675
+ ```vue
676
+ <script setup>
677
+ import { useRouterTabs } from 'vue3-router-tab'
678
+
679
+ const controller = useRouterTabs()
680
+
681
+ // Access current state
682
+ console.log(controller.tabs) // Array of all tabs
683
+ console.log(controller.activeId.value) // Current active tab ID
684
+ console.log(controller.includeKeys) // KeepAlive include keys
685
+ </script>
686
+ ```
687
+
688
+ ## Events
689
+
690
+ RouterTab emits events for tab interactions.
691
+
692
+ ### Available Events
693
+
694
+ | Event | Payload | Description |
695
+ |-------|---------|-------------|
696
+ | `tab-sort` | `{ tab, index }` | Fired when tab drag starts |
697
+ | `tab-sorted` | `{ tab, fromIndex, toIndex }` | Fired when tab is dropped in new position |
698
+
699
+ ### Usage Example
700
+
701
+ ```vue
702
+ <template>
703
+ <router-tab
704
+ @tab-sort="onTabSort"
705
+ @tab-sorted="onTabSorted"
706
+ />
707
+ </template>
708
+
709
+ <script setup>
710
+ function onTabSort({ tab, index }) {
711
+ console.log('Dragging tab:', tab.title, 'from index:', index)
712
+ }
713
+
714
+ function onTabSorted({ tab, fromIndex, toIndex }) {
715
+ console.log('Tab moved:', tab.title)
716
+ console.log('From:', fromIndex, 'To:', toIndex)
717
+
718
+ // Save new order to backend
719
+ saveTabOrder(tab, toIndex)
720
+ }
721
+ </script>
722
+ ```
723
+
397
724
  ## Slots
398
725
 
399
726
  - `start` / `end` – positioned on either side of the tab list (ideal for toolbars or the `<router-tabs>` helper).
@@ -401,7 +728,64 @@ Pass `false` to disable the context menu entirely.
401
728
 
402
729
  ## Styling
403
730
 
404
- The package ships with its own CSS bundle (imported automatically). Override the `router-tab__*` classes in your stylesheet to customise the appearance.
731
+ The package ships with its own CSS bundle (imported automatically). Override CSS custom properties or the `router-tab__*` classes to customize the appearance.
732
+
733
+ ### CSS Custom Properties
734
+
735
+ ```css
736
+ :root {
737
+ /* Layout */
738
+ --router-tab-header-height: 48px;
739
+ --router-tab-padding: 16px;
740
+ --router-tab-font-size: 14px;
741
+
742
+ /* Colors */
743
+ --router-tab-primary: #0f172a;
744
+ --router-tab-background: #ffffff;
745
+ --router-tab-text: #334155;
746
+ --router-tab-border: #e2e8f0;
747
+ --router-tab-active-background: #0f172a;
748
+ --router-tab-active-text: #ffffff;
749
+
750
+ /* Icons & Buttons */
751
+ --router-tab-icon-color: #64748b;
752
+ --router-tab-button-background: #f1f5f9;
753
+ --router-tab-button-color: #0f172a;
754
+ }
755
+ ```
756
+
757
+ ### Custom Styles Example
758
+
759
+ ```css
760
+ /* Change tab height */
761
+ .router-tab__header {
762
+ height: 56px;
763
+ }
764
+
765
+ /* Custom tab hover effect */
766
+ .router-tab__item:hover {
767
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
768
+ color: white;
769
+ }
770
+
771
+ /* Custom active tab */
772
+ .router-tab__item.is-active {
773
+ background: #4f46e5;
774
+ border-radius: 8px 8px 0 0;
775
+ }
776
+
777
+ /* Custom close button */
778
+ .router-tab__item-close {
779
+ border-radius: 4px;
780
+ }
781
+
782
+ /* Dark theme adjustments */
783
+ [data-theme="dark"] .router-tab {
784
+ --router-tab-background: #1e293b;
785
+ --router-tab-text: #f1f5f9;
786
+ --router-tab-border: #334155;
787
+ }
788
+ ```
405
789
 
406
790
  ## Types
407
791
 
@@ -409,6 +793,133 @@ The package ships with its own CSS bundle (imported automatically). Override the
409
793
  import type { TabRecord, RouterTabsSnapshot, RouterTabsPersistenceOptions } from 'vue3-router-tab'
410
794
  ```
411
795
 
796
+ ## Examples
797
+
798
+ Check out the [example-app](./example-app) directory for comprehensive demos including:
799
+
800
+ - **Basic Usage** - Simple tab navigation
801
+ - **Dynamic Titles** - Reactive tab title updates ([/title-test](./example-app/src/views/TitleTestDemo.vue))
802
+ - **Transitions** - All 7 transition effects ([/transition-demo](./example-app/src/views/TransitionDemo.vue))
803
+ - **Composables** - Using helper composables ([/composable-demo](./example-app/src/views/ComposableDemo.vue))
804
+ - **Advanced Features** - Sorting, context menus, persistence
805
+
806
+ ### Run Examples
807
+
808
+ ```bash
809
+ cd example-app
810
+ npm install
811
+ npm run dev
812
+ ```
813
+
814
+ ## Troubleshooting
815
+
816
+ ### Tab titles not updating
817
+
818
+ **Problem**: Tab title doesn't change when component state updates.
819
+
820
+ **Solution**: Ensure you're using reactive refs or computed properties:
821
+
822
+ ```vue
823
+ <!-- ✅ Correct -->
824
+ <script setup>
825
+ const routeTabTitle = ref('My Page') // Reactive
826
+ </script>
827
+
828
+ <!-- ❌ Wrong -->
829
+ <script setup>
830
+ const routeTabTitle = 'My Page' // Plain string - won't update
831
+ </script>
832
+ ```
833
+
834
+ ### Transitions not working
835
+
836
+ **Problem**: Page transitions don't show when refreshing tabs.
837
+
838
+ **Solution**: Make sure you're not using a custom default slot that overrides transitions. Remove the custom slot or include transition components:
839
+
840
+ ```vue
841
+ <!-- ✅ Default behavior with transitions -->
842
+ <router-tab page-transition="router-tab-fade" />
843
+
844
+ <!-- ❌ Custom slot without transitions -->
845
+ <router-tab>
846
+ <template #default="{ Component }">
847
+ <component :is="Component" /> <!-- No transition! -->
848
+ </template>
849
+ </router-tab>
850
+
851
+ <!-- ✅ Custom slot with transitions -->
852
+ <router-tab>
853
+ <template #default="{ Component }">
854
+ <transition name="router-tab-fade" mode="out-in">
855
+ <component :is="Component" />
856
+ </transition>
857
+ </template>
858
+ </router-tab>
859
+ ```
860
+
861
+ ### Tabs not persisting on refresh
862
+
863
+ **Problem**: Tabs are lost when refreshing the browser.
864
+
865
+ **Solution**: Add the `cookie-key` prop:
866
+
867
+ ```vue
868
+ <router-tab cookie-key="my-app-tabs" />
869
+ ```
870
+
871
+ Check that cookies are enabled in the browser and not being blocked.
872
+
873
+ ### KeepAlive not working
874
+
875
+ **Problem**: Component state is lost when switching tabs.
876
+
877
+ **Solution**: Ensure `:keep-alive="true"` (default) and components are properly keyed:
878
+
879
+ ```ts
880
+ // In router config
881
+ meta: {
882
+ keepAlive: true, // Enable for this route
883
+ key: 'fullPath' // Unique key per instance
884
+ }
885
+ ```
886
+
887
+ ### TypeScript errors
888
+
889
+ **Problem**: TypeScript shows errors for router tab properties.
890
+
891
+ **Solution**: Import types and use them:
892
+
893
+ ```ts
894
+ import type { TabRecord, RouterTabsOptions } from 'vue3-router-tab'
895
+
896
+ const options: RouterTabsOptions = {
897
+ keepAlive: true,
898
+ maxAlive: 10
899
+ }
900
+ ```
901
+
902
+ ## Browser Support
903
+
904
+ - Chrome/Edge ≥ 90
905
+ - Firefox ≥ 88
906
+ - Safari ≥ 14
907
+ - Modern mobile browsers
908
+
909
+ ## Contributing
910
+
911
+ Contributions are welcome! Please read the [contributing guidelines](CONTRIBUTING.md) first.
912
+
913
+ 1. Fork the repository
914
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
915
+ 3. Commit your changes (`git commit -m 'Add amazing feature'`)
916
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
917
+ 5. Open a Pull Request
918
+
412
919
  ## License
413
920
 
414
921
  MIT
922
+
923
+ ---
924
+
925
+ Made with ❤️ by the Vue community