How to use Bootstrap with Vue

Using Bootstrap with Vue provides a robust foundation for responsive web applications with pre-built components, grid system, and utility classes for rapid development. As the creator of CoreUI, a widely used open-source UI library, I’ve extensively used Bootstrap with Vue across enterprise applications, leveraging its mature ecosystem and component libraries like BootstrapVue for seamless integration. From my expertise, the most effective approach is to install Bootstrap CSS and optionally use BootstrapVue for Vue-specific components. This method provides access to Bootstrap’s complete styling system, responsive utilities, and component ecosystem while maintaining Vue’s reactive data binding and component architecture.

Install Bootstrap CSS and optionally BootstrapVue for Vue-optimized components and seamless integration.

# Method 1: Bootstrap CSS only
npm install bootstrap

# Method 2: Bootstrap with BootstrapVue (Vue 3)
npm install bootstrap@5 @popperjs/core
npm install bootstrap-vue-next

# Method 3: Bootstrap with PrimeVue (alternative)
npm install bootstrap primevue

# Method 4: CDN approach (for quick prototyping)
# Add to index.html:
# <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
# <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
// main.js - Bootstrap CSS only approach
import { createApp } from 'vue'
import App from './App.vue'

// Import Bootstrap CSS
import 'bootstrap/dist/css/bootstrap.min.css'
// Import Bootstrap JavaScript (optional)
import 'bootstrap/dist/js/bootstrap.bundle.min.js'

createApp(App).mount('#app')
// main.js - BootstrapVue approach
import { createApp } from 'vue'
import App from './App.vue'

// Import Bootstrap CSS
import 'bootstrap/dist/css/bootstrap.min.css'

// Import BootstrapVue Next
import { BootstrapVueNext } from 'bootstrap-vue-next'
import 'bootstrap-vue-next/dist/bootstrap-vue-next.css'

const app = createApp(App)
app.use(BootstrapVueNext)
app.mount('#app')
<template>
  <div class="container-fluid">
    <!-- Navigation -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-primary">
      <div class="container">
        <a class="navbar-brand" href="#">{{ appName }}</a>
        <button
          class="navbar-toggler"
          type="button"
          :class="{ collapsed: !navbarCollapsed }"
          @click="toggleNavbar"
          aria-expanded="false"
        >
          <span class="navbar-toggler-icon"></span>
        </button>

        <div class="collapse navbar-collapse" :class="{ show: navbarCollapsed }">
          <ul class="navbar-nav ms-auto">
            <li class="nav-item" v-for="item in navItems" :key="item.id">
              <a class="nav-link" :href="item.href" :class="{ active: item.active }">
                {{ item.text }}
              </a>
            </li>
          </ul>
        </div>
      </div>
    </nav>

    <!-- Hero Section -->
    <div class="bg-light py-5">
      <div class="container">
        <div class="row align-items-center">
          <div class="col-lg-6">
            <h1 class="display-4 fw-bold text-primary">{{ heroTitle }}</h1>
            <p class="lead text-muted">{{ heroSubtitle }}</p>
            <button class="btn btn-primary btn-lg me-3" @click="handlePrimaryAction">
              Get Started
            </button>
            <button class="btn btn-outline-secondary btn-lg" @click="handleSecondaryAction">
              Learn More
            </button>
          </div>
          <div class="col-lg-6">
            <img :src="heroImage" class="img-fluid rounded" alt="Hero Image">
          </div>
        </div>
      </div>
    </div>

    <!-- Content Grid -->
    <div class="container my-5">
      <div class="row">
        <div class="col-md-4 mb-4" v-for="feature in features" :key="feature.id">
          <div class="card h-100 shadow-sm" :class="cardClasses">
            <div class="card-body">
              <div class="text-center mb-3">
                <i :class="`bi bi-${feature.icon} text-primary`" style="font-size: 2rem;"></i>
              </div>
              <h5 class="card-title text-center">{{ feature.title }}</h5>
              <p class="card-text text-muted">{{ feature.description }}</p>
            </div>
            <div class="card-footer bg-transparent border-0">
              <button class="btn btn-outline-primary w-100" @click="selectFeature(feature)">
                Learn More
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Form Example -->
    <div class="container mb-5">
      <div class="row justify-content-center">
        <div class="col-lg-6">
          <div class="card shadow">
            <div class="card-header bg-primary text-white">
              <h5 class="mb-0">Contact Form</h5>
            </div>
            <div class="card-body">
              <form @submit.prevent="submitForm">
                <div class="mb-3">
                  <label for="name" class="form-label">Name *</label>
                  <input
                    v-model="form.name"
                    type="text"
                    class="form-control"
                    :class="{ 'is-invalid': errors.name }"
                    id="name"
                    required
                  >
                  <div v-if="errors.name" class="invalid-feedback">
                    {{ errors.name }}
                  </div>
                </div>

                <div class="mb-3">
                  <label for="email" class="form-label">Email *</label>
                  <input
                    v-model="form.email"
                    type="email"
                    class="form-control"
                    :class="{ 'is-valid': form.email && !errors.email }"
                    id="email"
                    required
                  >
                </div>

                <div class="mb-3">
                  <label for="message" class="form-label">Message</label>
                  <textarea
                    v-model="form.message"
                    class="form-control"
                    id="message"
                    rows="4"
                  ></textarea>
                </div>

                <div class="d-grid gap-2">
                  <button type="submit" class="btn btn-primary" :disabled="isSubmitting">
                    <span v-if="isSubmitting" class="spinner-border spinner-border-sm me-2"></span>
                    {{ isSubmitting ? 'Submitting...' : 'Send Message' }}
                  </button>
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- Footer -->
    <footer class="bg-dark text-light py-4">
      <div class="container">
        <div class="row">
          <div class="col-md-6">
            <p>&copy; 2025 {{ appName }}. All rights reserved.</p>
          </div>
          <div class="col-md-6 text-md-end">
            <p>Built with Vue.js and Bootstrap</p>
          </div>
        </div>
      </div>
    </footer>
  </div>
</template>

<script>
export default {
  name: 'BootstrapVueExample',
  data() {
    return {
      appName: 'Vue Bootstrap App',
      heroTitle: 'Build Amazing Apps',
      heroSubtitle: 'Combine the power of Vue.js with Bootstrap\'s responsive design system',
      heroImage: 'https://via.placeholder.com/600x400',
      navbarCollapsed: false,
      isSubmitting: false,
      navItems: [
        { id: 1, text: 'Home', href: '#', active: true },
        { id: 2, text: 'About', href: '#', active: false },
        { id: 3, text: 'Services', href: '#', active: false },
        { id: 4, text: 'Contact', href: '#', active: false }
      ],
      features: [
        {
          id: 1,
          title: 'Responsive Design',
          description: 'Mobile-first responsive grid system and components',
          icon: 'phone'
        },
        {
          id: 2,
          title: 'Vue Integration',
          description: 'Seamless integration with Vue.js reactive system',
          icon: 'code-square'
        },
        {
          id: 3,
          title: 'Rich Components',
          description: 'Extensive library of pre-built UI components',
          icon: 'grid-3x3'
        }
      ],
      form: {
        name: '',
        email: '',
        message: ''
      },
      errors: {}
    }
  },
  computed: {
    cardClasses() {
      return 'border-0 transition-shadow'
    }
  },
  methods: {
    toggleNavbar() {
      this.navbarCollapsed = !this.navbarCollapsed
    },
    handlePrimaryAction() {
      console.log('Primary action clicked')
    },
    handleSecondaryAction() {
      console.log('Secondary action clicked')
    },
    selectFeature(feature) {
      console.log('Selected feature:', feature.title)
    },
    async submitForm() {
      this.isSubmitting = true
      this.errors = {}

      // Simple validation
      if (!this.form.name.trim()) {
        this.errors.name = 'Name is required'
      }

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

        if (Object.keys(this.errors).length === 0) {
          console.log('Form submitted:', this.form)
          this.resetForm()
        }
      } catch (error) {
        console.error('Form submission error:', error)
      } finally {
        this.isSubmitting = false
      }
    },
    resetForm() {
      this.form = { name: '', email: '', message: '' }
      this.errors = {}
    }
  }
}
</script>

<style scoped>
.transition-shadow {
  transition: box-shadow 0.15s ease-in-out;
}

.transition-shadow:hover {
  box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
}
</style>

Bootstrap with Vue provides responsive design, utility classes, and component structure through CSS classes and optional JavaScript components. For Vue-specific implementations, BootstrapVue offers Vue components with reactive props and events. Use Bootstrap’s grid system (container, row, col) for layouts, utility classes for spacing and colors, and form classes for validation states. JavaScript components like modals and dropdowns can be controlled through Vue’s reactive data. Bootstrap’s CSS-only approach gives maximum flexibility while BootstrapVue provides convenient Vue components.

Best Practice Note:

This is the same approach we use for integrating Bootstrap with Vue in CoreUI enterprise applications and component libraries. Choose between CSS-only Bootstrap for flexibility or BootstrapVue for convenience, leverage Vue’s computed properties for dynamic classes, use Bootstrap’s utility classes for rapid styling, and maintain component isolation with scoped styles when needed.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.

Answers by CoreUI Core Team