Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Vue and Bootstrap: How to show different components based on media breakpoint? What's latest consensus?

I'm doing my first "mobile-first" approach to an app (normally started at desktop and worked my way down in the past using CSS Media queries) and wondering what is the best way to handle the following scenario in today's responsive dev landscape:

I have a component like so:

<b-form-spinbutton id="sb-vertical" v-model="value" inline></b-form-spinbutton>

I want the above component to only show when devices are below a certain breakpoint (lets say below 720px).

Then, any viewport bigger than that I want to show the following component:

<b-form-spinbutton id="sb-vertical" v-model="value" vertical></b-form-spinbutton>

Notice the vertical and inline props in the above codes.

Are these best left to the traditional CSS Media queries or are we able to use template conditionals (ie, v-if? etc) .

I figured I'd get a consensus because I am going to build my app from the ground up based on any feedback received to this quesiton. Thank you!

like image 876
redshift Avatar asked Oct 22 '25 07:10

redshift


1 Answers

I wrote a basic util (with room for improvement), that creates an object. Which can be imported and used in templates. For when you want to reactivly change props based on the current breakpoint.

The object contains various breakpoints, and if it's the currently active breakpoint. Simliarly it contains a nested lt (less than) and gt (greater than) object, which can be used when you want something to be shown if above or below a certain breakpoint.

Note that this hasn't been tested for SSR (Nuxt and simliar), and might not work since it accesses window.

BreakpointsUtil.js

import Vue from "vue";

const state = Vue.observable({
  screen: {}
});

/* This assumes you're using default bootstrap breakpoint names */
/* You need to hardcode the breakpoint values if you want to support IE11 */
const style = getComputedStyle(document.body);
const xs = style.getPropertyValue("--breakpoint-xs").replace("px", "");
const sm = style.getPropertyValue("--breakpoint-sm").replace("px", "");
const md = style.getPropertyValue("--breakpoint-md").replace("px", "");
const lg = style.getPropertyValue("--breakpoint-lg").replace("px", "");
const xl = style.getPropertyValue("--breakpoint-xl").replace("px", "");

function onResize() {
  const width = window.innerWidth;

  /* Not really sure how to properly define gt or lt */
  state.screen = {
    xs: width >= xs && width < sm,
    sm: width >= sm && width < md,
    md: width >= md && width < lg,
    lg: width >= lg && width < xl,
    xl: width >= xl,
    /* Greater than */
    gt: {
      xs: width >= xs,
      sm: width >= sm,
      md: width >= md,
      lg: width >= lg,
      xl: width >= xl
    },
    /* Less than */
    lt: {
      xs: width < sm,
      sm: width < md,
      md: width < lg,
      lg: width < xl,
      xl: width < 9999
    }
  };
}

/* Might want to debounce the event, to limit amount of calls */
window.onresize = onResize;
onResize();

export default state;

Which can then be imported like this (couldn't find a better way to expose it to the template and stay reactive)

RandomComponent.vue

<template>
 <!-- Some HTML -->
</template>

<script>
import breakpoints from "@/utils/breakpoints";

export default {
  computed: {
    breakpoints: () => breakpoints.screen
  }
};
</script>

Example snippet (rewritten slightly to fit the snippet)

/* BreakpointUtil.js */
const state = Vue.observable({
  screen: {}
});

/* This assumes you're using default bootstrap breakpoint names */
/* You need to hardcode the breakpoint values if you want to support IE11 */
const style = getComputedStyle(document.body);
const xs = style.getPropertyValue("--breakpoint-xs").replace("px", "");
const sm = style.getPropertyValue("--breakpoint-sm").replace("px", "");
const md = style.getPropertyValue("--breakpoint-md").replace("px", "");
const lg = style.getPropertyValue("--breakpoint-lg").replace("px", "");
const xl = style.getPropertyValue("--breakpoint-xl").replace("px", "");

function onResize() {
  const width = window.innerWidth;

  /* Not really sure how to properly define gt or lt */
  state.screen = {
    xs: width >= xs && width < sm,
    sm: width >= sm && width < md,
    md: width >= md && width < lg,
    lg: width >= lg && width < xl,
    xl: width >= xl,
    /* Greater than */
    gt: {
      xs: width >= xs,
      sm: width >= sm,
      md: width >= md,
      lg: width >= lg,
      xl: width >= xl
    },
    /* Less than */
    lt: {
      xs: width < sm,
      sm: width < md,
      md: width < lg,
      lg: width < xl,
      xl: width < 9999
    }
  };
}

/* Might want to debounce the event, to limit amount of calls */
window.onresize = onResize;
onResize();
/* BreakpointUtil.js END */

new Vue({
  el: "#app",
  computed: {
    screen: () => state.screen 
  }
});
<link href="https://unpkg.com/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://unpkg.com/[email protected]/dist/bootstrap-vue.css" rel="stylesheet" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.11/vue.js"></script>
<script src="https://unpkg.com/[email protected]/dist/bootstrap-vue.js"></script>

<div id="app">
  <b-card no-body>
    <b-tabs pills card :vertical="screen.lt.sm">
      <b-tab title="Tab 1" active>
        <b-card-text>Tab contents 1</b-card-text>
      </b-tab>
      <b-tab title="Tab 2"><b-card-text>Tab contents 2</b-card-text></b-tab>
      <b-tab title="Tab 3"><b-card-text>Tab contents 3</b-card-text></b-tab>
    </b-tabs>
  </b-card>
</div>
like image 142
Hiws Avatar answered Oct 24 '25 23:10

Hiws



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!