Vue.js Samples

Vue.js component examples including Composition API, Options API, reactive state, and modern Vue patterns

💻 Vue.js Hello World javascript

🟢 simple

Basic Vue.js component examples and Hello World applications with template syntax

⏱️ 15 min 🏷️ vue, components, template, reactivity
Prerequisites: JavaScript basics, HTML basics
// Vue.js Hello World Examples

// 1. Basic Vue 3 app with Composition API
import { createApp } from 'vue';

const HelloWorld = {
    template: `
        <div>
            <h1>Hello, World!</h1>
            <p>This is a Vue.js component.</p>
        </div>
    `
};

createApp(HelloWorld).mount('#app');

// 2. Vue component with reactive data (Composition API)
import { createApp, ref } from 'vue';

const Counter = {
    setup() {
        const count = ref(0);

        const increment = () => {
            count.value++;
        };

        const decrement = () => {
            count.value--;
        };

        return {
            count,
            increment,
            decrement
        };
    },
    template: `
        <div>
            <h1>Count: {{ count }}</h1>
            <button @click="increment">+</button>
            <button @click="decrement">-</button>
        </div>
    `
};

// 3. Vue component with Options API
const UserForm = {
    data() {
        return {
            name: '',
            email: '',
            submitted: false
        };
    },
    methods: {
        submitForm() {
            if (this.name && this.email) {
                this.submitted = true;
                setTimeout(() => {
                    this.submitted = false;
                    this.name = '';
                    this.email = '';
                }, 3000);
            }
        }
    },
    template: `
        <div>
            <h2>User Form</h2>
            <form @submit.prevent="submitForm">
                <div>
                    <label>Name:</label>
                    <input v-model="name" type="text" required>
                </div>
                <div>
                    <label>Email:</label>
                    <input v-model="email" type="email" required>
                </div>
                <button type="submit">Submit</button>
            </form>
            <div v-if="submitted" class="success">
                Form submitted successfully! Name: {{ name }}, Email: {{ email }}
            </div>
        </div>
    `
};

// 4. Conditional rendering and list rendering
const TodoList = {
    data() {
        return {
            todos: [
                { id: 1, text: 'Learn Vue.js', completed: false },
                { id: 2, text: 'Build a project', completed: false },
                { id: 3, text: 'Deploy to production', completed: true }
            ],
            newTodo: '',
            filter: 'all'
        };
    },
    computed: {
        filteredTodos() {
            switch (this.filter) {
                case 'completed':
                    return this.todos.filter(todo => todo.completed);
                case 'active':
                    return this.todos.filter(todo => !todo.completed);
                default:
                    return this.todos;
            }
        },
        activeTodosCount() {
            return this.todos.filter(todo => !todo.completed).length;
        }
    },
    methods: {
        addTodo() {
            if (this.newTodo.trim()) {
                this.todos.push({
                    id: Date.now(),
                    text: this.newTodo.trim(),
                    completed: false
                });
                this.newTodo = '';
            }
        },
        toggleTodo(id) {
            const todo = this.todos.find(t => t.id === id);
            if (todo) {
                todo.completed = !todo.completed;
            }
        },
        deleteTodo(id) {
            this.todos = this.todos.filter(t => t.id !== id);
        }
    },
    template: `
        <div>
            <h2>Todo List ({{ activeTodosCount }} remaining)</h2>

            <div class="todo-form">
                <input
                    v-model="newTodo"
                    @keyup.enter="addTodo"
                    placeholder="Add new todo..."
                >
                <button @click="addTodo">Add</button>
            </div>

            <div class="filters">
                <button
                    @click="filter = 'all'"
                    :class="{ active: filter === 'all' }"
                >
                    All
                </button>
                <button
                    @click="filter = 'active'"
                    :class="{ active: filter === 'active' }"
                >
                    Active
                </button>
                <button
                    @click="filter = 'completed'"
                    :class="{ active: filter === 'completed' }"
                >
                    Completed
                </button>
            </div>

            <ul class="todo-list">
                <li
                    v-for="todo in filteredTodos"
                    :key="todo.id"
                    :class="{ completed: todo.completed }"
                >
                    <input
                        type="checkbox"
                        v-model="todo.completed"
                        @change="toggleTodo(todo.id)"
                    >
                    <span>{{ todo.text }}</span>
                    <button @click="deleteTodo(todo.id)" class="delete">Delete</button>
                </li>
            </ul>
        </div>
    `
};

// 5. Component with computed properties and watchers
const PriceCalculator = {
    data() {
        return {
            price: 100,
            quantity: 1,
            taxRate: 0.08,
            discountCode: '',
            discounts: {
                'SAVE10': 0.1,
                'SAVE20': 0.2,
                'SAVE30': 0.3
            }
        };
    },
    computed: {
        subtotal() {
            return this.price * this.quantity;
        },
        discount() {
            if (this.discounts[this.discountCode.toUpperCase()]) {
                return this.subtotal * this.discounts[this.discountCode.toUpperCase()];
            }
            return 0;
        },
        discountedSubtotal() {
            return this.subtotal - this.discount;
        },
        tax() {
            return this.discountedSubtotal * this.taxRate;
        },
        total() {
            return this.discountedSubtotal + this.tax;
        }
    },
    watch: {
        discountCode(newCode) {
            if (newCode && !this.discounts[newCode.toUpperCase()]) {
                console.log('Invalid discount code');
            }
        }
    },
    methods: {
        applyDiscount(code) {
            this.discountCode = code;
        }
    },
    template: `
        <div>
            <h2>Price Calculator</h2>

            <div class="form-group">
                <label>Price per item:</label>
                <input v-model.number="price" type="number" min="0">
            </div>

            <div class="form-group">
                <label>Quantity:</label>
                <input v-model.number="quantity" type="number" min="1">
            </div>

            <div class="form-group">
                <label>Discount Code:</label>
                <input v-model="discountCode" placeholder="Enter code">
                <div class="discount-buttons">
                    <button @click="applyDiscount('SAVE10')">SAVE10</button>
                    <button @click="applyDiscount('SAVE20')">SAVE20</button>
                    <button @click="applyDiscount('SAVE30')">SAVE30</button>
                </div>
            </div>

            <div class="breakdown">
                <h3>Price Breakdown:</h3>
                <p>Subtotal: ${{ subtotal.toFixed(2) }}</p>
                <p v-if="discount > 0">Discount: ${{ discount.toFixed(2) }}</p>
                <p>Tax (${{ (taxRate * 100).toFixed(0) }}%): ${{ tax.toFixed(2) }}</p>
                <hr>
                <p><strong>Total: ${{ total.toFixed(2) }}</strong></p>
            </div>
        </div>
    `
};

// 6. Component with event handling and slots
const Card = {
    props: {
        title: {
            type: String,
            required: true
        },
        variant: {
            type: String,
            default: 'default',
            validator: (value) => ['default', 'primary', 'secondary', 'success'].includes(value)
        }
    },
    computed: {
        cardClass() {
            return ['card', `card-${this.variant}`];
        }
    },
    emits: ['close'],
    methods: {
        closeCard() {
            this.$emit('close');
        }
    },
    template: `
        <div :class="cardClass">
            <div class="card-header">
                <h3>{{ title }}</h3>
                <button v-if="$slots.close" @click="closeCard" class="close-btn">
                    <slot name="close">✕</slot>
                </button>
            </div>
            <div class="card-body">
                <slot></slot>
            </div>
            <div v-if="$slots.footer" class="card-footer">
                <slot name="footer"></slot>
            </div>
        </div>
    `
};

// 7. Dynamic components
const DynamicComponentExample = {
    data() {
        return {
            currentComponent: 'welcome',
            components: {
                welcome: 'WelcomeComponent',
                profile: 'ProfileComponent',
                settings: 'SettingsComponent'
            }
        };
    },
    components: {
        WelcomeComponent: {
            template: '<h2>Welcome to the Dashboard!</h2>'
        },
        ProfileComponent: {
            template: '<h2>User Profile</h2><p>This is your profile page.</p>'
        },
        SettingsComponent: {
            template: '<h2>Settings</h2><p>Configure your preferences here.</p>'
        }
    },
    template: `
        <div>
            <div class="navigation">
                <button
                    v-for="(component, key) in components"
                    :key="key"
                    @click="currentComponent = key"
                    :class="{ active: currentComponent === key }"
                >
                    {{ key.charAt(0).toUpperCase() + key.slice(1) }}
                </button>
            </div>

            <div class="content">
                <component :is="components[currentComponent]" />
            </div>
        </div>
    `
};

// 8. Component with lifecycle hooks
const LifecycleExample = {
    data() {
        return {
            message: 'Component is active',
            counter: 0,
            intervalId: null
        };
    },
    created() {
        console.log('Component created');
        this.startCounter();
    },
    mounted() {
        console.log('Component mounted to DOM');
        console.log('Element:', this.$el);
    },
    beforeUpdate() {
        console.log('Component is about to update');
    },
    updated() {
        console.log('Component updated');
    },
    beforeUnmount() {
        console.log('Component is about to be unmounted');
        this.stopCounter();
    },
    unmounted() {
        console.log('Component unmounted');
    },
    methods: {
        startCounter() {
            this.intervalId = setInterval(() => {
                this.counter++;
            }, 1000);
        },
        stopCounter() {
            if (this.intervalId) {
                clearInterval(this.intervalId);
            }
        }
    },
    template: `
        <div>
            <h3>Lifecycle Hook Example</h3>
            <p>{{ message }}</p>
            <p>Counter: {{ counter }}</p>
            <p>Check the console for lifecycle events!</p>
        </div>
    `
};

// 9. Component with provide/inject
const ThemeProvider = {
    data() {
        return {
            theme: 'light',
            themes: {
                light: {
                    background: '#ffffff',
                    color: '#000000',
                    primary: '#007bff'
                },
                dark: {
                    background: '#1a1a1a',
                    color: '#ffffff',
                    primary: '#0056b3'
                }
            }
        };
    },
    provide() {
        return {
            theme: this.theme,
            themes: this.themes,
            toggleTheme: this.toggleTheme
        };
    },
    methods: {
        toggleTheme() {
            this.theme = this.theme === 'light' ? 'dark' : 'light';
        }
    },
    template: `
        <div class="theme-provider">
            <slot></slot>
        </div>
    `
};

const ThemedComponent = {
    inject: ['theme', 'themes', 'toggleTheme'],
    template: `
        <div :style="{
            backgroundColor: themes[theme].background,
            color: themes[theme].color
        }">
            <h3>Current Theme: {{ theme }}</h3>
            <p>This component uses the injected theme.</p>
            <button
                @click="toggleTheme"
                :style="{ backgroundColor: themes[theme].primary }"
            >
                Toggle Theme
            </button>
        </div>
    `
};

// 10. Complete application example
const App = {
    components: {
        Card,
        Counter,
        UserForm,
        TodoList,
        PriceCalculator,
        DynamicComponentExample,
        LifecycleExample,
        ThemedComponent
    },
    data() {
        return {
            activeSection: 'welcome'
        };
    },
    methods: {
        showSection(section) {
            this.activeSection = section;
        }
    },
    template: `
        <div class="app">
            <header>
                <h1>Vue.js Examples</h1>
                <nav>
                    <button
                        v-for="section in ['welcome', 'counter', 'form', 'todos', 'calculator', 'dynamic', 'lifecycle', 'themed']"
                        :key="section"
                        @click="showSection(section)"
                        :class="{ active: activeSection === section }"
                    >
                        {{ section.charAt(0).toUpperCase() + section.slice(1) }}
                    </button>
                </nav>
            </header>

            <main>
                <!-- Welcome Section -->
                <section v-if="activeSection === 'welcome'">
                    <Card title="Welcome to Vue.js">
                        <p>This is a collection of Vue.js examples demonstrating various features.</p>
                        <template #footer>
                            <p>Click the navigation buttons to explore different examples.</p>
                        </template>
                    </Card>
                </section>

                <!-- Counter Section -->
                <section v-if="activeSection === 'counter'">
                    <Card title="Counter Component">
                        <Counter />
                    </Card>
                </section>

                <!-- Form Section -->
                <section v-if="activeSection === 'form'">
                    <Card title="User Form">
                        <UserForm />
                    </Card>
                </section>

                <!-- Todo List Section -->
                <section v-if="activeSection === 'todos'">
                    <Card title="Todo List">
                        <TodoList />
                    </Card>
                </section>

                <!-- Price Calculator Section -->
                <section v-if="activeSection === 'calculator'">
                    <Card title="Price Calculator">
                        <PriceCalculator />
                    </Card>
                </section>

                <!-- Dynamic Component Section -->
                <section v-if="activeSection === 'dynamic'">
                    <Card title="Dynamic Components">
                        <DynamicComponentExample />
                    </Card>
                </section>

                <!-- Lifecycle Section -->
                <section v-if="activeSection === 'lifecycle'">
                    <Card title="Lifecycle Hooks">
                        <LifecycleExample />
                    </Card>
                </section>

                <!-- Themed Component Section -->
                <section v-if="activeSection === 'themed'">
                    <ThemeProvider>
                        <Card title="Theme System">
                            <ThemedComponent />
                        </Card>
                    </ThemeProvider>
                </section>
            </main>
        </div>
    `
};

export {
    HelloWorld,
    Counter,
    UserForm,
    TodoList,
    PriceCalculator,
    Card,
    DynamicComponentExample,
    LifecycleExample,
    ThemeProvider,
    ThemedComponent,
    App
};

💻 Vue.js Composition API javascript

🟡 intermediate ⭐⭐⭐⭐

Advanced Vue.js Composition API examples including ref, reactive, computed, watch, and custom composables

⏱️ 40 min 🏷️ vue, composition api, reactivity, hooks
Prerequisites: Vue.js basics, JavaScript ES6+, Reactivity concepts
// Vue.js Composition API Examples

import { createApp, ref, reactive, computed, watch, watchEffect, onMounted, onUnmounted, nextTick, provide, inject } from 'vue';

// 1. Basic ref and reactive
const BasicReactivity = {
    setup() {
        // ref for primitive values
        const count = ref(0);
        const message = ref('Hello Vue 3!');

        // reactive for objects
        const user = reactive({
            name: 'John Doe',
            age: 30,
            email: '[email protected]'
        });

        const increment = () => {
            count.value++;
        };

        const updateUser = () => {
            user.age++;
            user.email = `john${user.age}@example.com`;
        };

        return {
            count,
            message,
            user,
            increment,
            updateUser
        };
    },
    template: `
        <div>
            <h2>Basic Reactivity</h2>
            <p>Count: {{ count }}</p>
            <button @click="increment">Increment</button>

            <h3>User Info</h3>
            <p>Name: {{ user.name }}</p>
            <p>Age: {{ user.age }}</p>
            <p>Email: {{ user.email }}</p>
            <button @click="updateUser">Update Age</button>
        </div>
    `
};

// 2. Computed properties
const ComputedExample = {
    setup() {
        const price = ref(100);
        const quantity = ref(2);
        const tax = ref(0.08);

        // Read-only computed
        const subtotal = computed(() => price.value * quantity.value);
        const taxAmount = computed(() => subtotal.value * tax.value);
        const total = computed(() => subtotal.value + taxAmount.value);

        // Writable computed
        const totalWithDiscount = computed({
            get: () => total.value,
            set: (newValue) => {
                const discount = total.value - newValue;
                const discountPercentage = discount / total.value;
                tax.value = Math.max(0, 0.08 - discountPercentage);
            }
        });

        return {
            price,
            quantity,
            tax,
            subtotal,
            taxAmount,
            total,
            totalWithDiscount
        };
    },
    template: `
        <div>
            <h2>Computed Properties</h2>
            <div class="form-group">
                <label>Price: ${{ price }}</label>
                <input v-model.number="price" type="range" min="0" max="1000">
            </div>
            <div class="form-group">
                <label>Quantity: {{ quantity }}</label>
                <input v-model.number="quantity" type="range" min="1" max="10">
            </div>
            <div class="form-group">
                <label>Tax Rate: {{ (tax * 100).toFixed(1) }}%</label>
                <input v-model.number="tax" type="range" min="0" max="0.3" step="0.01">
            </div>

            <div class="breakdown">
                <p>Subtotal: ${{ subtotal.toFixed(2) }}</p>
                <p>Tax: ${{ taxAmount.toFixed(2) }}</p>
                <p><strong>Total: ${{ total.toFixed(2) }}</strong></p>

                <h4>Writable Computed Example:</h4>
                <p>Desired Total: ${{ totalWithDiscount.toFixed(2) }}</p>
                <input
                    v-model.number="totalWithDiscount"
                    type="number"
                    :max="total"
                    step="10"
                >
            </div>
        </div>
    `
};

// 3. Watch and watchEffect
const WatchExample = {
    setup() {
        const searchQuery = ref('');
        const results = ref([]);
        const loading = ref(false);
        const searchHistory = ref([]);

        // Watch specific source
        watch(
            searchQuery,
            async (newQuery, oldQuery) => {
                if (newQuery.trim()) {
                    loading.value = true;

                    // Simulate API call
                    await new Promise(resolve => setTimeout(resolve, 500));

                    // Mock search results
                    results.value = [
                        `Result 1 for "${newQuery}"`,
                        `Result 2 for "${newQuery}"`,
                        `Result 3 for "${newQuery}"`
                    ];

                    searchHistory.value.push({
                        query: newQuery,
                        timestamp: new Date().toLocaleTimeString()
                    });

                    loading.value = false;
                } else {
                    results.value = [];
                }
            },
            { debounce: 300 }
        );

        // WatchEffect for reactive dependencies
        const searchStats = computed(() => ({
            queryLength: searchQuery.value.length,
            resultsCount: results.value.length,
            historyCount: searchHistory.value.length
        }));

        return {
            searchQuery,
            results,
            loading,
            searchHistory,
            searchStats
        };
    },
    template: `
        <div>
            <h2>Watch and WatchEffect</h2>
            <div class="search-box">
                <input
                    v-model="searchQuery"
                    placeholder="Search for something..."
                    :disabled="loading"
                >
                <span v-if="loading">Searching...</span>
            </div>

            <div v-if="results.length > 0" class="results">
                <h3>Results:</h3>
                <ul>
                    <li v-for="result in results" :key="result">{{ result }}</li>
                </ul>
            </div>

            <div class="stats">
                <h3>Search Statistics:</h3>
                <p>Query length: {{ searchStats.queryLength }}</p>
                <p>Results found: {{ searchStats.resultsCount }}</p>
                <p>Search history: {{ searchStats.historyCount }}</p>
            </div>

            <div v-if="searchHistory.length > 0" class="history">
                <h3>Search History:</h3>
                <ul>
                    <li v-for="item in searchHistory.slice(-5)" :key="item.timestamp">
                        {{ item.query }} ({{ item.timestamp }})
                    </li>
                </ul>
            </div>
        </div>
    `
};

// 4. Custom composables
function useCounter(initialValue = 0) {
    const count = ref(initialValue);

    const increment = () => {
        count.value++;
    };

    const decrement = () => {
        count.value--;
    };

    const reset = () => {
        count.value = initialValue;
    };

    const setValue = (newValue) => {
        count.value = newValue;
    };

    return {
        count: readonly(count),
        increment,
        decrement,
        reset,
        setValue
    };
}

function useToggle(initialState = false) {
    const state = ref(initialState);

    const toggle = () => {
        state.value = !state.value;
    };

    const setTrue = () => {
        state.value = true;
    };

    const setFalse = () => {
        state.value = false;
    };

    return {
        state: readonly(state),
        toggle,
        setTrue,
        setFalse
    };
}

function useLocalStorage(key, initialValue) {
    const storedValue = ref(initialValue);

    // Load from localStorage on mount
    if (typeof window !== 'undefined') {
        const item = localStorage.getItem(key);
        if (item) {
            try {
                storedValue.value = JSON.parse(item);
            } catch (e) {
                console.error('Error parsing localStorage item:', e);
            }
        }

        // Watch for changes and save to localStorage
        watch(
            storedValue,
            (newValue) => {
                localStorage.setItem(key, JSON.stringify(newValue));
            },
            { deep: true }
        );
    }

    const setValue = (value) => {
        storedValue.value = value;
    };

    const removeValue = () => {
        if (typeof window !== 'undefined') {
            localStorage.removeItem(key);
        }
        storedValue.value = initialValue;
    };

    return {
        value: storedValue,
        setValue,
        removeValue
    };
}

const ComposablesExample = {
    setup() {
        // Use counter composable
        const { count: counter1, increment: inc1, decrement: dec1, reset: reset1 } = useCounter(10);
        const { count: counter2, increment: inc2, decrement: dec2, reset: reset2 } = useCounter(0);

        // Use toggle composable
        const { state: isDarkMode, toggle: toggleTheme } = useToggle(false);
        const { state: isVisible, toggle: toggleVisibility } = useToggle(true);

        // Use localStorage composable
        const { value: userName, setValue: setUserName } = useLocalStorage('userName', 'Guest');
        const { value: preferences, setValue: setPreferences } = useLocalStorage('preferences', {
            notifications: true,
            theme: 'light'
        });

        return {
            counter1,
            inc1,
            dec1,
            reset1,
            counter2,
            inc2,
            dec2,
            reset2,
            isDarkMode,
            toggleTheme,
            isVisible,
            toggleVisibility,
            userName,
            setUserName,
            preferences,
            setPreferences
        };
    },
    template: `
        <div :class="{ 'dark-mode': isDarkMode }">
            <h2>Custom Composables</h2>

            <!-- Counter Examples -->
            <div class="section">
                <h3>Counters</h3>
                <div class="counter-group">
                    <div>
                        <p>Counter 1: {{ counter1 }}</p>
                        <button @click="inc1">+</button>
                        <button @click="dec1">-</button>
                        <button @click="reset1">Reset</button>
                    </div>
                    <div>
                        <p>Counter 2: {{ counter2 }}</p>
                        <button @click="inc2">+</button>
                        <button @click="dec2">-</button>
                        <button @click="reset2">Reset</button>
                    </div>
                </div>
            </div>

            <!-- Toggle Examples -->
            <div class="section">
                <h3>Toggles</h3>
                <p>Dark Mode: {{ isDarkMode ? 'ON' : 'OFF' }}</p>
                <button @click="toggleTheme">Toggle Dark Mode</button>

                <p>Content Visible: {{ isVisible ? 'YES' : 'NO' }}</p>
                <button @click="toggleVisibility">Toggle Visibility</button>

                <div v-if="isVisible" class="toggle-content">
                    This content can be toggled on and off!
                </div>
            </div>

            <!-- LocalStorage Examples -->
            <div class="section">
                <h3>LocalStorage</h3>
                <div>
                    <label>User Name:</label>
                    <input v-model="userName" @input="setUserName($event.target.value)">
                    <p>Persisted: {{ userName }}</p>
                </div>

                <div>
                    <label>Notifications:</label>
                    <input
                        type="checkbox"
                        v-model="preferences.notifications"
                        @change="setPreferences(preferences)"
                    >

                    <label>Theme:</label>
                    <select
                        v-model="preferences.theme"
                        @change="setPreferences(preferences)"
                    >
                        <option value="light">Light</option>
                        <option value="dark">Dark</option>
                    </select>

                    <p>Preferences: {{ JSON.stringify(preferences) }}</p>
                </div>
            </div>
        </div>
    `
};

// 5. Lifecycle hooks in Composition API
const LifecycleComposableExample = {
    setup() {
        const data = ref('Initial data');
        const mountedTime = ref(null);
        const updateCount = ref(0);
        let intervalId = null;

        onMounted(() => {
            console.log('Component mounted');
            mountedTime.value = new Date().toLocaleTimeString();

            // Start a timer
            intervalId = setInterval(() => {
                updateCount.value++;
            }, 1000);
        });

        onUnmounted(() => {
            console.log('Component unmounted');
            if (intervalId) {
                clearInterval(intervalId);
            }
        });

        const updateData = async () => {
            data.value = 'Updating...';
            await nextTick(); // Wait for DOM update
            data.value = `Updated at ${new Date().toLocaleTimeString()}`;
        };

        return {
            data,
            mountedTime,
            updateCount,
            updateData
        };
    },
    template: `
        <div>
            <h2>Lifecycle Hooks</h2>
            <p>Mounted at: {{ mountedTime }}</p>
            <p>Update count: {{ updateCount }}</p>
            <p>Data: {{ data }}</p>
            <button @click="updateData">Update Data</button>
            <p>Check console for lifecycle events</p>
        </div>
    `
};

// 6. Provide and Inject
const ThemeProviderComposable = {
    setup() {
        const theme = ref('light');
        const themes = reactive({
            light: {
                primary: '#007bff',
                background: '#ffffff',
                color: '#000000'
            },
            dark: {
                primary: '#0056b3',
                background: '#1a1a1a',
                color: '#ffffff'
            }
        });

        const toggleTheme = () => {
            theme.value = theme.value === 'light' ? 'dark' : 'light';
        };

        provide('theme', theme);
        provide('themes', themes);
        provide('toggleTheme', toggleTheme);

        return {
            theme,
            toggleTheme
        };
    },
    template: `
        <div class="theme-provider">
            <slot></slot>
        </div>
    `
};

const ThemedComponentComposable = {
    setup() {
        const theme = inject('theme');
        const themes = inject('themes');
        const toggleTheme = inject('toggleTheme');

        return {
            theme,
            themes,
            toggleTheme
        };
    },
    template: `
        <div
            :style="{
                backgroundColor: themes[theme].background,
                color: themes[theme].color,
                padding: '20px',
                borderRadius: '8px'
            }"
        >
            <h3>Themed Component</h3>
            <p>Current theme: {{ theme }}</p>
            <button
                @click="toggleTheme"
                :style="{
                    backgroundColor: themes[theme].primary,
                    color: themes[theme].background,
                    border: 'none',
                    padding: '8px 16px',
                    borderRadius: '4px'
                }"
            >
                Toggle Theme
            </button>
        </div>
    `
};

// 7. Teleport example
const TeleportExample = {
    setup() {
        const showModal = ref(false);

        const openModal = () => {
            showModal.value = true;
        };

        const closeModal = () => {
            showModal.value = false;
        };

        return {
            showModal,
            openModal,
            closeModal
        };
    },
    template: `
        <div>
            <h2>Teleport Example</h2>
            <button @click="openModal">Open Modal</button>

            <Teleport to="body">
                <div v-if="showModal" class="modal-overlay">
                    <div class="modal">
                        <h3>Teleported Modal</h3>
                        <p>This modal is rendered at the document body level.</p>
                        <button @click="closeModal">Close</button>
                    </div>
                </div>
            </Teleport>
        </div>
    `
};

export {
    BasicReactivity,
    ComputedExample,
    WatchExample,
    ComposablesExample,
    LifecycleComposableExample,
    ThemeProviderComposable,
    ThemedComponentComposable,
    TeleportExample,
    useCounter,
    useToggle,
    useLocalStorage
};

💻 Vue.js Router and Pinia javascript

🔴 complex ⭐⭐⭐⭐⭐

Vue.js application patterns with Vue Router for navigation and Pinia for state management

⏱️ 50 min 🏷️ vue, router, pinia, spa, state
Prerequisites: Vue.js Composition API, State management concepts, Routing concepts
// Vue.js Router and Pinia Examples

import { createRouter, createWebHistory } from 'vue-router';
import { createPinia, defineStore } from 'pinia';
import { ref, computed, watch } from 'vue';

// 1. Pinia Store Examples

// User Store
export const useUserStore = defineStore('user', () => {
    // State
    const user = ref(null);
    const isLoggedIn = computed(() => !!user.value);
    const userRole = computed(() => user.value?.role || 'guest');

    // Actions
    const login = async (credentials) => {
        // Simulate API call
        await new Promise(resolve => setTimeout(resolve, 1000));

        user.value = {
            id: 1,
            name: credentials.name,
            email: credentials.email,
            role: 'user',
            token: 'mock-jwt-token'
        };

        return user.value;
    };

    const logout = () => {
        user.value = null;
    };

    const updateProfile = (profileData) => {
        if (user.value) {
            user.value = { ...user.value, ...profileData };
        }
    };

    return {
        user,
        isLoggedIn,
        userRole,
        login,
        logout,
        updateProfile
    };
});

// Product Store
export const useProductStore = defineStore('products', () => {
    // State
    const products = ref([]);
    const loading = ref(false);
    const error = ref(null);
    const searchQuery = ref('');
    const selectedCategory = ref('all');

    // Getters
    const filteredProducts = computed(() => {
        let filtered = products.value;

        // Filter by search query
        if (searchQuery.value) {
            filtered = filtered.filter(product =>
                product.name.toLowerCase().includes(searchQuery.value.toLowerCase()) ||
                product.description.toLowerCase().includes(searchQuery.value.toLowerCase())
            );
        }

        // Filter by category
        if (selectedCategory.value !== 'all') {
            filtered = filtered.filter(product => product.category === selectedCategory.value);
        }

        return filtered;
    });

    const categories = computed(() => {
        const cats = new Set(products.value.map(p => p.category));
        return ['all', ...Array.from(cats)];
    });

    const productById = (id) => {
        return computed(() => products.value.find(p => p.id === parseInt(id)));
    };

    // Actions
    const fetchProducts = async () => {
        loading.value = true;
        error.value = null;

        try {
            // Simulate API call
            await new Promise(resolve => setTimeout(resolve, 1500));

            products.value = [
                { id: 1, name: 'Laptop', price: 999, category: 'electronics', description: 'Powerful laptop for work and gaming', stock: 10 },
                { id: 2, name: 'Smartphone', price: 699, category: 'electronics', description: 'Latest smartphone with great camera', stock: 25 },
                { id: 3, name: 'Headphones', price: 199, category: 'electronics', description: 'Noise-cancelling wireless headphones', stock: 15 },
                { id: 4, name: 'Coffee Maker', price: 149, category: 'home', description: 'Automatic coffee maker', stock: 8 },
                { id: 5, name: 'Desk Chair', price: 299, category: 'furniture', description: 'Ergonomic office chair', stock: 5 },
                { id: 6, name: 'Bookshelf', price: 199, category: 'furniture', description: 'Wooden bookshelf', stock: 3 }
            ];
        } catch (err) {
            error.value = 'Failed to fetch products';
        } finally {
            loading.value = false;
        }
    };

    const addProduct = (product) => {
        const newProduct = {
            ...product,
            id: Math.max(...products.value.map(p => p.id)) + 1
        };
        products.value.push(newProduct);
    };

    const updateProduct = (id, updates) => {
        const index = products.value.findIndex(p => p.id === parseInt(id));
        if (index !== -1) {
            products.value[index] = { ...products.value[index], ...updates };
        }
    };

    const deleteProduct = (id) => {
        products.value = products.value.filter(p => p.id !== parseInt(id));
    };

    return {
        products,
        loading,
        error,
        searchQuery,
        selectedCategory,
        filteredProducts,
        categories,
        productById,
        fetchProducts,
        addProduct,
        updateProduct,
        deleteProduct
    };
});

// Cart Store
export const useCartStore = defineStore('cart', () => {
    // State
    const items = ref([]);
    const total = computed(() => {
        return items.value.reduce((sum, item) => sum + (item.price * item.quantity), 0);
    });
    const itemCount = computed(() => {
        return items.value.reduce((sum, item) => sum + item.quantity, 0);
    });

    // Actions
    const addToCart = (product, quantity = 1) => {
        const existingItem = items.value.find(item => item.id === product.id);

        if (existingItem) {
            existingItem.quantity += quantity;
        } else {
            items.value.push({
                id: product.id,
                name: product.name,
                price: product.price,
                quantity
            });
        }
    };

    const removeFromCart = (productId) => {
        items.value = items.value.filter(item => item.id !== productId);
    };

    const updateQuantity = (productId, quantity) => {
        const item = items.value.find(item => item.id === productId);
        if (item) {
            if (quantity <= 0) {
                removeFromCart(productId);
            } else {
                item.quantity = quantity;
            }
        }
    };

    const clearCart = () => {
        items.value = [];
    };

    return {
        items,
        total,
        itemCount,
        addToCart,
        removeFromCart,
        updateQuantity,
        clearCart
    };
});

// 2. Vue Router Configuration
const routes = [
    {
        path: '/',
        name: 'Home',
        component: {
            template: `
                <div class="home">
                    <h1>Welcome to Vue Shop</h1>
                    <p>Browse our products or check your cart</p>
                </div>
            `
        }
    },
    {
        path: '/products',
        name: 'Products',
        component: {
            setup() {
                const productStore = useProductStore();
                const cartStore = useCartStore();

                onMounted(() => {
                    productStore.fetchProducts();
                });

                return {
                    productStore,
                    cartStore
                };
            },
            template: `
                <div class="products">
                    <h1>Products</h1>

                    <div class="filters">
                        <input
                            v-model="productStore.searchQuery"
                            placeholder="Search products..."
                        >
                        <select v-model="productStore.selectedCategory">
                            <option v-for="category in productStore.categories" :key="category" :value="category">
                                {{ category.charAt(0).toUpperCase() + category.slice(1) }}
                            </option>
                        </select>
                    </div>

                    <div v-if="productStore.loading" class="loading">
                        Loading products...
                    </div>

                    <div v-else-if="productStore.error" class="error">
                        {{ productStore.error }}
                    </div>

                    <div v-else class="product-grid">
                        <div v-for="product in productStore.filteredProducts" :key="product.id" class="product-card">
                            <h3>{{ product.name }}</h3>
                            <p>{{ product.description }}</p>
                            <p class="price">${{ product.price }}</p>
                            <p class="category">{{ product.category }}</p>
                            <p class="stock">Stock: {{ product.stock }}</p>
                            <button
                                @click="cartStore.addToCart(product)"
                                :disabled="product.stock === 0"
                            >
                                Add to Cart
                            </button>
                        </div>
                    </div>
                </div>
            `
        }
    },
    {
        path: '/product/:id',
        name: 'ProductDetail',
        component: {
            setup() {
                const route = useRoute();
                const productStore = useProductStore();
                const cartStore = useCartStore();

                const product = computed(() => productStore.productById(route.params.id));

                onMounted(() => {
                    if (productStore.products.length === 0) {
                        productStore.fetchProducts();
                    }
                });

                return {
                    product,
                    cartStore
                };
            },
            template: `
                <div v-if="product" class="product-detail">
                    <h1>{{ product.name }}</h1>
                    <p class="price">${{ product.price }}</p>
                    <p>{{ product.description }}</p>
                    <p>Category: {{ product.category }}</p>
                    <p>Stock: {{ product.stock }}</p>
                    <button
                        @click="cartStore.addToCart(product)"
                        :disabled="product.stock === 0"
                    >
                        Add to Cart
                    </button>
                </div>
                <div v-else>
                    Product not found
                </div>
            `
        }
    },
    {
        path: '/cart',
        name: 'Cart',
        component: {
            setup() {
                const cartStore = useCartStore();
                return { cartStore };
            },
            template: `
                <div class="cart">
                    <h1>Shopping Cart</h1>

                    <div v-if="cartStore.items.length === 0" class="empty-cart">
                        Your cart is empty
                    </div>

                    <div v-else>
                        <div class="cart-items">
                            <div v-for="item in cartStore.items" :key="item.id" class="cart-item">
                                <h3>{{ item.name }}</h3>
                                <p>${{ item.price }} each</p>
                                <div class="quantity-controls">
                                    <button @click="cartStore.updateQuantity(item.id, item.quantity - 1)">-</button>
                                    <span>{{ item.quantity }}</span>
                                    <button @click="cartStore.updateQuantity(item.id, item.quantity + 1)">+</button>
                                </div>
                                <button @click="cartStore.removeFromCart(item.id)" class="remove">Remove</button>
                            </div>
                        </div>

                        <div class="cart-summary">
                            <p>Total Items: {{ cartStore.itemCount }}</p>
                            <p>Total: ${{ cartStore.total.toFixed(2) }}</p>
                            <button @click="cartStore.clearCart">Clear Cart</button>
                            <button class="checkout">Checkout</button>
                        </div>
                    </div>
                </div>
            `
        }
    },
    {
        path: '/profile',
        name: 'Profile',
        component: {
            setup() {
                const userStore = useUserStore();
                const editing = ref(false);
                const form = ref({});

                const startEdit = () => {
                    form.value = { ...userStore.user };
                    editing.value = true;
                };

                const saveProfile = () => {
                    userStore.updateProfile(form.value);
                    editing.value = false;
                };

                const cancelEdit = () => {
                    editing.value = false;
                };

                return {
                    userStore,
                    editing,
                    form,
                    startEdit,
                    saveProfile,
                    cancelEdit
                };
            },
            template: `
                <div class="profile">
                    <h1>User Profile</h1>

                    <div v-if="!userStore.isLoggedIn">
                        <p>Please log in to view your profile</p>
                    </div>

                    <div v-else>
                        <div v-if="!editing">
                            <h2>{{ userStore.user.name }}</h2>
                            <p>Email: {{ userStore.user.email }}</p>
                            <p>Role: {{ userStore.userRole }}</p>
                            <button @click="startEdit">Edit Profile</button>
                            <button @click="userStore.logout">Logout</button>
                        </div>

                        <div v-else>
                            <input v-model="form.name" placeholder="Name">
                            <input v-model="form.email" placeholder="Email">
                            <button @click="saveProfile">Save</button>
                            <button @click="cancelEdit">Cancel</button>
                        </div>
                    </div>
                </div>
            `
        }
    },
    {
        path: '/login',
        name: 'Login',
        component: {
            setup() {
                const userStore = useUserStore();
                const router = useRouter();
                const credentials = ref({ name: '', email: '' });
                const loading = ref(false);

                const handleLogin = async () => {
                    loading.value = true;
                    try {
                        await userStore.login(credentials.value);
                        router.push('/');
                    } catch (error) {
                        console.error('Login failed:', error);
                    } finally {
                        loading.value = false;
                    }
                };

                return {
                    credentials,
                    loading,
                    handleLogin
                };
            },
            template: `
                <div class="login">
                    <h1>Login</h1>
                    <form @submit.prevent="handleLogin">
                        <input v-model="credentials.name" placeholder="Name" required>
                        <input v-model="credentials.email" type="email" placeholder="Email" required>
                        <button type="submit" :disabled="loading">
                            {{ loading ? 'Logging in...' : 'Login' }}
                        </button>
                    </form>
                </div>
            `
        }
    }
];

const router = createRouter({
    history: createWebHistory(),
    routes
});

// 3. Navigation Guards
router.beforeEach((to, from, next) => {
    const userStore = useUserStore();

    // Redirect to login if trying to access profile without being logged in
    if (to.name === 'Profile' && !userStore.isLoggedIn) {
        next({ name: 'Login' });
    } else {
        next();
    }
});

// 4. Main App Component
const App = {
    setup() {
        const userStore = useUserStore();
        const cartStore = useCartStore();
        const router = useRouter();
        const route = useRoute();

        const navigation = [
            { name: 'Home', path: '/' },
            { name: 'Products', path: '/products' },
            { name: 'Cart', path: '/cart' },
            { name: 'Profile', path: '/profile' }
        ];

        return {
            userStore,
            cartStore,
            router,
            route,
            navigation
        };
    },
    template: `
        <div class="app">
            <header>
                <nav>
                    <router-link
                        v-for="item in navigation"
                        :key="item.path"
                        :to="item.path"
                        :class="{ active: route.path === item.path }"
                    >
                        {{ item.name }}
                    </router-link>
                </nav>

                <div class="header-actions">
                    <router-link to="/cart" class="cart-link">
                        Cart ({{ cartStore.itemCount }})
                    </router-link>

                    <span v-if="userStore.isLoggedIn">
                        {{ userStore.user.name }}
                    </span>
                    <router-link v-else to="/login">
                        Login
                    </router-link>
                </div>
            </header>

            <main>
                <router-view />
            </main>

            <footer>
                <p>&copy; 2024 Vue Shop Demo</p>
            </footer>
        </div>
    `
};

export {
    useUserStore,
    useProductStore,
    useCartStore,
    router,
    App
};