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

  • 2
    I have the same problem as Nelly – Carl Kroeger Ihl Jul 19 '19 at 16:41
  • 6
    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

    With Vue.js 3.2 you can do State-Driven Dynamic CSS like this:

    <template>
        <h1 id="script">Script</h1>
        <h1 id="scriptSetup">Script setup</h1>
    </template>
    
    <script>
      export default {
        data() {
          return {
            colorFromScript: 'red'
          }
        }
      }
    </script>
    
    <script setup>
    const colorFromScriptSetup = 'green'
    </script>
    
    <style>
      #script {
          color: v-bind('colorFromScript')
      }
    
      #scriptSetup {
          color: v-bind('colorFromScriptSetup')
      }
    </style>
    

    See an implementation here

    Namysh
    • 1,984
    • 6
    • 16
    14

    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,030
    • 21
    • 100
    • 124
    Muthu Kumaran
    • 17,301
    • 5
    • 46
    • 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,030
    • 21
    • 100
    • 124
    Niklesh Raut
    • 31,857
    • 10
    • 68
    • 102
    • 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
    7

    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,030
    • 21
    • 100
    • 124
    Sebastian
    • 1,561
    • 2
    • 10
    • 11
    3

    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,030
    • 21
    • 100
    • 124
    John Fash
    • 317
    • 3
    • 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
    3

    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,461
    • 14
    • 19
    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,030
    • 21
    • 100
    • 124
    Laurent Meyer
    • 2,706
    • 3
    • 29
    • 54
    1

    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,030
    • 21
    • 100
    • 124
    Lohith
    • 730
    • 1
    • 8
    • 22
    0

    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
    • 46
    • 5