Vue.js dynamic – Nelly Sattari Apr 11 '19 at 01:55

  • 7
    This is the right idea, but as Nelly points out, Vue throws a warning or may even not load depending on version in use (minified Vue appears to allow this). See Muthu's answer (specifically their Update section) or [this SO answer](https://stackoverflow.com/a/52280182/3268060) for a better solution – Squimon Sep 20 '19 at 03:46
  • 1
    instead of putting the CSS variables in a ` – Silkster Oct 31 '20 at 20:11
  • Unfortunately HTML validators will throw an error for – Ali Seivani Jul 28 '21 at 14:18
  • 19

    CSS <style> is static. I don't think you can do that... you might have to look for a different approach.

    You can try using CSS variables. For example, (the code below is not tested)

    <template>
        <div class="class_name" :style="{'--bkgImage': 'url(' + project.background + ')', '--bkgImageMobile': 'url(' + project.backgroundRetina + ')'}">
        </div>
    </template>
    
    <style>
        .class_name{
            background-image: var(--bkgImage);
        }
        @media all and (-webkit-min-device-pixel-ratio : 1.5),
            all and (-o-min-device-pixel-ratio: 3/2),
            all and (min--moz-device-pixel-ratio: 1.5),
            all and (min-device-pixel-ratio: 1.5) {
                .class_name {
                    background-image: var(--bkgImageMobile);
                }
            }
    </style>
    

    Note: Only the latest browsers support CSS variables.

    If you still see any issues with the :style in the template then try this,

    <div :style="'--bkgImage: url(' + project.background + '); --bkgImageMobile: url(' + project.backgroundRetina + ')'">
    </div>
    
    Peter Mortensen
    • 30,738
    • 21
    • 105
    • 131
    Muthu Kumaran
    • 17,682
    • 5
    • 47
    • 70
    10

    As you are using Vue.js, use Vue.js to change the background, instead of CSS:

    var vm = new Vue({
        el: '#vue-instance',
        data: {
            rows: [
                {value: 'green'},
                {value: 'red'},
                {value: 'blue'},
            ],
            item:""
        },
        methods:{
            onTimeSlotClick: function(item){
                console.log(item);
                document.querySelector(".dynamic").style.background = item;
            }
        }
    });
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.16/vue.js"></script>
    <div id="vue-instance">
        <select class="form-control" v-model="item" v-on:change="onTimeSlotClick(item)">
            <option value="">Select</option>
            <option v-for="row in rows">
                {{row.value}}
            </option>
        </select>
        <div class='dynamic'>VALUE</div>
        <br/><br/>
        <div :style="{ background: item}">Another</div>
    </div>
    Peter Mortensen
    • 30,738
    • 21
    • 105
    • 131
    Niklesh Raut
    • 34,013
    • 16
    • 75
    • 109
    • 1
      Great but in an inline method, i cannot add a background for retina. I know that method. – Dominik Traskowski Nov 16 '17 at 06:42
    • You can add class, toggle, v-show, v-if to achieve this – Niklesh Raut Nov 16 '17 at 06:50
    • I can do it with two div and class to show one div depends of retina or not. But.. It will load two images always. v-if or v-show sound good but how to check if we are on a retina display with v-if? Or how to check it in JS.. Maybe i will found solution in that way. – Dominik Traskowski Nov 16 '17 at 07:20
    9

    Yes, this is possible. Vue.js does not support style tags in templates, but you can get around this by using a component tag. Untested pseudocode:

    In your template:

    <component type="style" v-html="style"></component>
    

    In your script:

    props: {
        color: String
    }
    computed: {
        style() {
            return `.myJSGeneratedStyle { color: ${this.color} }`;
        }
    }
    

    There are lots of reasons why you shouldn't use this method. It's definitely hacky and :style="" is probably better most of the time, but for your problem with media queries I think this is a good solution.

    Peter Mortensen
    • 30,738
    • 21
    • 105
    • 131
    Sebastian
    • 1,808
    • 2
    • 12
    • 13
    6

    Vue 3 State-Driven Dynamic CSS Variables

    I know this is a bit late and is using Vue.js 2, but as of now in Vue.js 3 you can create state-driven CSS variables.

    You can now use your SFC (Single File Component) state data inside your styles tags using v-bind().

    You can read more about state-driven CSS variables here, or read the Vue.js 3 documentation here.

    Here is a code example

    Example

    <template>
      <div>
        <input type="text" v-model="color" />
        <div class="user-input-color">
          {{ color }}
        </div>
      </div>
    </template>
    
    <script>
    export default {
      data: () => ({
        color: 'white'
      })
    }
    </script>
    
    <style scoped>
    .user-input-color {
      background-color: v-bind(color)
    }
    </style>
    

    Here is a link to the live example.

    Links

    Fletcher Rippon
    • 1,837
    • 16
    • 21
    4

    You can use the component tag offered by Vue.js.

    <template>
      <component :is="`style`">
        .cg {color: {{color}};}
      </component>
      <p class="cg">I am green</p> <br/>
      <button @click="change">change</button>
    </template>
    <script>
      export default {
        data(){
          return { color: 'green' }
        },
        methods: {
          change() {this.color = 'red';}
        }
      }
    </script>
    Peter Mortensen
    • 30,738
    • 21
    • 105
    • 131
    Johnson Fashanu
    • 897
    • 8
    • 6
    • Even if this could work, this Sz.'s answer here suggests that it might not be a good practice: https://stackoverflow.com/a/50154225/14008447 – Alan Kersaudy Mar 16 '22 at 14:11
    2

    I encountered the same problem and I figured out a hack which suits my needs (and maybe yours).

    As <style> is contained in <head>, there is a way to make it work:

    We generate the CSS content as a computed property based on the state of the page/component

    computed: {
        css() {
            return `<style type="text/css">
             .bg {
                background: ${this.bg_color_string};
             }</style>`
        }
    }
    

    Now, we have our style as a string and the only challenge is to pass it to the browser.

    I added this to my <head>

    <style id="customStyle"></style>
    

    Then I call the setInterval once the page is loaded.

     mounted() {
         setInterval(() => this.refreshHead(), 1000);
     }
    

    And I define the refreshHead as such:

    methods: {
       refreshHead() {
           document.getElementById('customStyle').innerHTML = this.css
       }
    }
    
    Peter Mortensen
    • 30,738
    • 21
    • 105
    • 131
    Laurent Meyer
    • 2,766
    • 3
    • 33
    • 57
    1

    I needed to write completely dynamic styles, so I used approach beyond Vue system:

    {
       // Other properties.
        watch: {
            myProp: {
                handler() {
                    this.styleElement.innerHTML = this.myProp.css;
                },
                deep: true,
            },
        },
        mounted() {
            this.styleElement = this.document.createElement('style');
            this.styleElement.innerText = this.myProp.css;
            this.document.head.append(this.styleElement);
        },
        unmounted() {
            this.styleElement.remove();
        },
    }
    

    Though it may have some performace issues with CSS big enough.

    MikhailM
    • 71
    • 5
    0

    In simple terms, this is how you would do it in Vue.js and Nuxt.js:

    <template>
        <div>
            <img :src="dynamicImageURL" alt="" :style="'background-color':backgroundColor"/>
        </div>
    </template>
    
    <script>
        export default{
            data(){
               return {
                     dynamicImageURL='myimage.png',
                     backgroundColor='red',
                }
            }
        }
    </script>
    
    Peter Mortensen
    • 30,738
    • 21
    • 105
    • 131
    Lohith
    • 866
    • 1
    • 9
    • 25
    0

    I liked @mickey-mullin reply, but not everything worked entirely. The url missed require, even though the information in his post helped me a lot in my case.

    var(), url(), multiple ternary operators (my own case - you shouldn't need it), I was able to do so for background-image in such a way:

    template

    <div :style="[
      case1 ? { '--iconUrl': `url(${require('../../../public/icon1.svg')})`} :
      case2 ? { '--iconUrl': `url(${require('../../../public/icon2.svg')})`} :
      { '--iconUrl': `url(${require('../../../public/default.svg')})` },
    ]" class="myClass">
    

    styles

    div.myClass::before {
      background-image: var(--iconUrl);
    }
    

    Note: I didn't have to declare iconUrl in my data() -> return.

    Daniel Danielecki
    • 8,508
    • 6
    • 68
    • 94
    0

    This is simple to do, even better if already using a framework / css variables.

    By adding style to your html that would overwrite the root/variables according to loaded "dynamic css"

    In that case using css vars in your css

    <style>
    .myTheme{
      color:var(--theme-color)
    }
    </style>
    

    then having a computed property like:

    computed:{
     accountTheme(){
      return {"--theme-color":this.themeColor}
     }
    }
    

    Then whatever method that would change such dynamic theme you do it

    methods:{
     load(){
      ....
      this.themeColor="red"
     }
    }
    

    Apply that dynamic style to html element

    <html :style="accountTheme"></html>
    
    Miguel
    • 3,349
    • 2
    • 32
    • 28
    0

    This works for me - if you add the style tags along with the definitions and it should work.

    <template>
        <div class="wrap">
            <div v-html="customStyle"></div>
        </div>
    </template>
    
    <script setup>
    import { ref } from "vue";
    const customStyle = ref("<style>body{background:blue!important;}</style>");
    </script>
    
    
    Jon Freynik
    • 359
    • 2
    • 15