How can I import a svg file to a Vue component? - javascript

In vue single file component.I import a svg file like this:
import A from 'a.svg'
And then how can I use A in my component?

Based on the information you provided, what you can do is:
Install vue-svg-loader
npm install --save-dev vue-svg-loader
Configure webpack:
module: {
rules: [
{
test: /\.svg$/,
loader: 'vue-svg-loader', // `vue-svg` for webpack 1.x
},
],
},
Import the svg and use it as a regular component:
<template>
<nav id="menu">
<a href="...">
<SomeIcon class="icon" />
Some page
</a>
</nav>
</template>
<script>
import SomeIcon from './assets/some-icon.svg';
export default {
name: 'menu',
components: {
SomeIcon,
},
};
</script>
Reference: https://github.com/visualfanatic/vue-svg-loader

I've gotten the following to work in Vue 3. Doesn't require messing with webpack or installing any third party plugins.
<template>
<img :src="mySVG" />
</template>
<script>
export default {
name: 'App',
data(){
return {
mySVG: require('./assets/my-svg-file.svg')
}
}
}
</script>
Note: I'm aware that you cannot modify certain pieces of the SVG when using it in img src, but if you simply want to use SVG files like you would any other image, this seems to be a quick and easy solution.

If you have control over the svg file, you can just wrap it in a vue file like so:
a.vue:
<template>
<svg>...</svg>
</template>
Just require the file like this afterwards: import A from 'a.vue'

If you are using Webpack you can use the require context to load SVG files from a directory. Be aware that this will put all SVG files within your Javascript files and might bloat your code though.
As a simplified example I am using this svg component:
data() {
return {
svg: ''
};
},
props: {
name: {
type: String,
required: true
}
}
created() {
this.svg = require(`resources/assets/images/svg/${this.name}.svg`);
}
The template simply looks like this:
<template>
<div :class="classes" v-html="svg"></div>
</template>
Normally you can't simply load SVG files like that and expect them to be used with a v-html directive since you are not getting the raw output. You have to use the Webpack raw-loader so make sure you get the raw output:
{
test: /\.svg$/,
use: [
{
loader: 'raw-loader',
query: {
name: 'images/svg/[name].[ext]'
}
},
{
loader: 'svgo-loader',
options: svgoConfig
}
]
}
The example above also uses the svgo-loader since you will want to heavily optimize your SVG files if you do down this route.
Hopefully this help you or anyone else out on how to solve this without diving straight into a third-party solution to fix this.

I would just use vue-svg
Install via Vue CLI 3:
vue add svg
Input:
<img src="#/assets/logo.svg?data" />
Output:
<img src="data:image/svg+xml;base64,..." />
or this is work also...
import LogoImage from "#/assets/logo.svg?inline"

You can also use something like this:
<template>
<img :src="logo"></img>
</template>
<script>
import logo from '../assets/img/logo.svg'
export default {
data() {
return {
logo
}
}
}
</script>
This doesn't require installing external modules and works out of the box.

I like to use pug as a template engine (comes with many advantages) - if you do so, you will be able to easily include files like SVG's just by writing:
include ../assets/some-icon.svg
That's it! there is nothing else to do - I think this is an very easy and convenient way to include stuff like smaller svg's - file's easily included code is still clean!
Here you can get some more information how to include PugJS into you Vue instance https://www.npmjs.com/package/vue-cli-plugin-pug

First you need a specific loader for the component which will contain the svg
my webpack.base.config.js
module: {
rules: [
{
test: /\.svg$/,
loader: 'vue-svg-loader',
},
{
test: /\.vue$/,
use: [
{
loader: "vue-loader",
options: vueLoaderConfig
},
{
loader: "vue-svg-inline-loader",
options: { /* ... */ }
}
]
}
//.. your other rules
}
docs of vues-svg-inline-loader : https://www.npmjs.com/package/vue-svg-inline-loader
docs of vue-svg-loader : https://www.npmjs.com/package/vue-svg-loader
Next, you can initialise a vue file
<template>
<div>
<img svg-inline class="icon" src='../pathtoyourfile/yoursvgfile.svg' alt="example" />
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'logo',
data () {
},
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#logo{
width:20%;
}
.rounded-card{
border-radius:15px;
}
//the style of your svg
//look for it in your svg file ..
//example
.cls-1,.cls-7{isolation:isolate;}.cls-2{fill:url(#linear-gradient);}.cls-3{fill:url(#linear-gradient-2);};stroke-width:2px;}..cls-6{opacity:0.75;mix-blend-mode:multiply;}.cls-7{opacity:0.13;}.cls-8{fill:#ed6a29;}.cls-9{fill:#e2522b;}.cls-10{fill:#ed956e;}.cls-185{fill:#ffc933;}..cls-13{fill:#ffd56e;}.cls-14{fill:#1db4d8;}.cls-15{fill:#0f9fb7;}.cls-16{fill:#3ad4ed;}.cls-17{fill:#25bdde;}.cls-18{fill:#fff;}
//
</style>
Your svg fils must dont contain style tag so copy paste the style in the vue style with scoped propoerty to keep it specific to this component
you can just load you component in specific place of your app
and use it
<template>
<v-app id="app">
<logo/>
<router-view/>
</v-app>
</template>
<script>
import logo from './components/logo.vue'
export default {
name: 'App',
data(){
return {
//your data
}
},
components:{
logo //the name of the component you imported
},
}
}
</script>
<style>
#app {
font-family: 'Hellow', sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #070b0f;
margin-top: 60px;
}
</style>

You can always save it as a .svg file in your /static/svg/myfile.svg (using webpack) and just use it as an image file: <img src="/static/svg/myfile.svg">. No require / import / loader needed.

+1 for #Stephan-v's solution, but here's a slightly modified approach for 2021 with Webpack 5.
Your Vue component <template/>
Option A: Single SVG file
<template>
<svg viewBox="0 0 24 24">
<use :xlink:href="require('#/assets/icons/icon.svg')"></use>
</svg>
</template>
Option B: SVG Sprite (e.g. for FeatherIcons)
<template>
<svg viewBox="0 0 24 24">
<use
:xlink:href="require('#/assets/icons/sprite.svg') + `#${iconName}`"
></use>
</svg>
</template>
<script>
export default {
props: {
// Dynamic property to easily switch out the SVG which will be used
iconName: {
type: String,
default: "star",
},
},
};
</script>
You may need a Webpack loader.
NOTE: You may not need the Webpack Loader if you're using Vue 3 (as mentioned above) or Vite. If you're using Storybook or Nuxt, you will likely still need it.
$ npm install svgo-loader -D
$ yarn add svgo-loader -D
webpack.config.js (or similar)
module.exports = {
mode: "development",
entry: "./foo.js",
output: {},
// ... other config ...
module: {
rules: [
/////////////
{
// Webpack 5 SVG loader
// https://webpack.js.org/guides/asset-modules/
// https://dev.to/smelukov/webpack-5-asset-modules-2o3h
test: /\.svg$/,
type: "asset",
use: "svgo-loader",
},
],
/////////////
},
};
Done!

I was able to get svgs loading inline via
<div v-html="svgStringHere"></div>
Where svgStringHere is a computed property that returns an svg as a string

Related

loading css in asynchronously imported javascript using webpack with MiniCssExtractPlugin

i'm working on a webapp that loads it's different pages asynchronously via dynamic imports e.g.:
import {Component, h} from "preact";
import AsyncRoute from "preact-async-route";
import {Layout} from "components/layout";
import {PageLoading} from "components/page-loading";
export class Page extends Component {
render() {
return (
<Layout>
<AsyncRoute
key="doorstation"
path={"/doorstation"}
getComponent={this.getSamplePage}
loading={() => <PageLoading />}
/>
</Layout>
);
}
async getSamplePage(){
const { init, Page } = await import("modules/sample");
init();
return Page;
}
}
and in the actual imported file i do
// modules/sample/page/index.tsx
import {Component, h} from "preact";
import styles from "./styles.css";
export class Page extends Component {... components logic}
// modules/sample/index.tsx
export const init = () => // some initialization logic
export { Page } from "./Page"
Each of those pages has their own css that gets imported on their respective file. What i'm stuck now with is that it puts the resulting css in it's own chunk but when the browser build tries to import said css it just fails with an error like:
GET http://<my-ip>/341.()=%3E%225028a51a47a787d4cc85%22.css net::ERR_ABORTED 404 (Not Found)
Error: Loading CSS chunk 341 failed.
(/341.()=>"5028a51a47a787d4cc85".css)
at o.<computed>.o.<computed>.<computed>.e.push.o.<computed>.d.onerror
on inspection of the import on the console the href of resulting stylesheet tag is actually set to:
link.href = "http://<my-ip>/341.()=%3E%225028a51a47a787d4cc85%22.css"
what i expected was that webpack would resolve those paths on my dynamically imported modules but apparently this isn't the case.
for now my config looks like this:
module: {
rules: [
test: /\.css$/,
use: [
{
loader: args.mode === "production" ? MiniCssExtractPlugin.loader : "style-loader",
},
{
loader: "css-loader",
options: {
sourceMap: args.mode !== "production",
importLoaders: 1,
modules: {
localIdentName:
args.mode === "production"
? "c-[hash:base64:8]"
: "[name]-[local]-[hash:base64:4]",
},
},
},
{
loader: "postcss-loader",
},
]
},
plugins: [
new MiniCssExtractPlugin({
filename: "[name].[hash:8].css",
chunkFilename: "[name].[hash:8].css",
}),
]
by just using the style-loader everything works just fine but this won't give me actual css chunks. I'm not entirely sure where or what to change so webpack will require the actual css chunk files instead of resolving the path with that... what looks like an function expression. I was already looking up the issue but so far it didn't really solve my issue since it usually revolved around undefined values or something.
Did i miss something in this config? is someone able to help?
Thank you in advance.

Cannot use import statement outside a module using nuxtjs

I am trying to use vue-agile carousel, I can install and get it to run without any issues right after i install it, i am using NUXT, but after restarting my server i keep getting this error and can not find any solution for it
<template>
<div>
<agile>
<div class="slide">
<img src="/img/img2.jpg" alt="" />
</div>
<div class="slide">
<img src="/img/img1.jpg" alt="" />
</div>
</agile>
</div>
</template>
<script >
import { VueAgile } from "vue-agile";
export default {
name: "",
layout: "",
middleware: [],
data() {
return {};
},
components: {
agile: VueAgile,
},
};
</script>
Did you checked the documentation about how to use this plugin in Nuxt instead of a regular Vue?
plugins/vue-agile.js
import Vue from 'vue'
import VueAgile from 'vue-agile'
Vue.use(VueAgile)
nuxt.config.js
export default {
plugins: ['~/plugins/vue-agile', mode: 'client']
}
To use component without SSR use the client-only component:
<client-only placeholder="Loading...">
<agile>...</agile>
</client-only>
EDIT: Add Shreerang's suggestion (comment below).
Sergio's answer above is mostly accurate, but needs a small tweek.
The nuxt.config.json config needs the following update. No build config is required.
plugins: [
{ src: '~/plugins/vue-agile', mode: 'client' }
]
You need to mark vue-agile to be transpiled in order to work on the server part (SSR).
nuxt.config.js :
...
build: {
transpile: [/vue-agile/]
}
...
from official Nuxt.js docs, they said.
If you get an Cannot use import statement outside a module error, you
may need to add your package to the build > transpile option in
nuxt.config.js for webpack loader to make your plugin available.
Example
module.exports = {
build: {
transpile: ['vue-agile']
}
}
Add type="module" in your script tag
<script type="module">
import { VueAgile } from "vue-agile";
export default {
name: "",
layout: "",
middleware: [],
data() {
return {};
},
components: {
agile: VueAgile,
},
};
</script>

Include pictures in JS bundle

My case is the following :
I create a components library for React. So I have a package (bundled with Rollup) that include some pictures (For now only a GIF picture that is used in a component).
The component that use my picture is like this :
import React from 'react';
import PropTypes from 'prop-types';
import ui_spinner from '../../../assets/ui_progress.gif';
/**
* CircularSpinner
* Add a spinner when the user needs to wait
*/
class CircularSpinner extends React.PureComponent {
static propTypes = {
/** Width of the component */
width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Height of the component */
height: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
/** Style of the component (overload existing properties) */
style: PropTypes.object,
}
static defaultProps = {
width: 128,
height: 128,
style: {},
}
render() {
const { style, width, height } = this.props;
return (
<img src={ui_spinner} width={width} height={height} style={style} alt="ui_progress" aria-busy="true" />
);
}
}
export default CircularSpinner;
When I built it, it's OK.
Now I create a React application with create-react-app and I want to test my components library. To do this, I use npm link (To avoid to push deploy my npm package). My components are OK in my testing application but the picture (the GIF in my CircularSpinner component) is not displayed.
So my question is the following : How to include some assets in a JS bundle with Rollup ? My working approach is correct ?
My Rollup config is the following :
import { uglify } from 'rollup-plugin-uglify'
import babel from 'rollup-plugin-babel'
import url from 'rollup-plugin-url'
const config = {
input: 'src/index.js',
external: ['react'],
output: {
format: 'umd',
name: 'react-components',
globals: {
react: "React"
}
},
plugins: [
babel({
exclude: "node_modules/**"
}),
uglify(),
url(),
]
}
export default config
I build with rollup -c -o dist/index.js.
And the dist folder has the following content :
dist/
assets
92be5c546b4adf43.gif
index.js
My component that use my picture is like this in my testing application :
<img src="92be5c546b4adf43.gif" width="128" height="128" alt="ui_progress" aria-busy="true">
Thanks for your help !
Damien
I found the solution for this issue. This response may help someone :
I update my rollup config to use rollup-plugin-img. I have already used it but my configuration was not correct :
The correct config is the following :
import { uglify } from 'rollup-plugin-uglify'
import babel from 'rollup-plugin-babel'
import image from 'rollup-plugin-img'
const config = {
input: 'src/index.js',
external: ['react'],
output: {
format: 'umd',
name: 'react-components',
globals: {
react: "React"
}
},
plugins: [
babel({
exclude: "node_modules/**"
}),
image({
limit: 100000,
}),
uglify(),
]
}
export default config
My error was that my GIF is a little big and the default limit size is 8192 bytes.
In this case, I have the following error :
Error: Could not load <path of image> (imported by <path of component that use image>): The "path" argument must be of type string. Received type undefined
When I have updated my config to increase the limit, everything is OK

Do I need to (or should I) wrap all my Vue components in a wrapper component?

I am trying to put together Webpack 4.5 and Vue components. The build is fine and I see on the screen the expected two components (details below).
Creating this basic SPA was a very iterative process, with information gathered from various sources (not always consistent). I finally end up with:
an HTML file which has one Vue component, a wrapper for the whole SPA
the Vue component above, which itself brings in the actual useful components
Is there a way to skip the wrapper component so that I can directly edit the HTML file (and link a CSS style file)? I would then define a CSS grid and place the components within.
Or is there an advantage to keep it this way I do not foresee?
The current project files:
the HTML file opened in the browser
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<infoscreen id="infoscreen"></infoscreen>
</body>
<script src="bundle.js"></script>
</html>
the webpack config file
'use strict'
const path = require('path');
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
mode: 'development',
entry: [
'./src/entry.js'
],
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader'
}
]
},
plugins: [
new CopyWebpackPlugin([{
from: 'src/infoscreen.html',
to: 'infoscreen.html'
}])
]
}
the entry.js file
import infoscreen from "./infoscreen.vue"
import Vue from "vue/dist/vue.js"
Vue.component("infoscreen", infoscreen)
new Vue({ el: "infoscreen" })
the Vue wrapper component (infoscreen.vue)
This is the file I would like to get rid of and use <temperature-outside></temperature-outside> directly in the HTML file above
<template>
<div id="app">
<temperature-outside></temperature-outside>
<temperature-outside></temperature-outside>
</div>
</template>
<script>
import temperatureOutside from './temperatureOutside.vue'
export default {
name: 'infoscreen',
components: {
'temperature-outside': temperatureOutside
}
}
</script>
the Vue component (temperatureOutside.vue)
<template>
<div>
hello {{yo}}
</div>
</template>
<script>
export default {
name: 'temperatureOutside',
data: function () {
return {
yo: "world"
}
}
}
</script>
Vue components are recognized within the context of a Vue instance. It turns out you will need a mounting point in your html which the wrapping or parent Vue component will mount all child components registered.
Registering Components

How to use external html templates in Components with Vue.js 2.0 in Laravel 5.3

I am using the default Laravel 5.3 setup - a fresh install with default configuration for Vue.js 2.0.
With Laravel 5.1 and Vue.js 1.x, I could easily define components like and used browserify to compile.
Vue.component('test-component', require('./test-component');
/* test-component.js */
export default{
template:require('./test-component.template.html')
}
/* test-component.template.html */
<div class="test-component">
<h1> Test Component <h1>
</div>
However, with Vue 2, the default is webpack (although browserify is also available but could not get it working and webpack seems better). But I am not able to get the configuration working.
I have tried various configurations, finally I have this in my gulp.js file
const elixir = require('laravel-elixir');
require('laravel-elixir-vue-2');
var config = {
module:{
loaders:[
{
test: /\.html$/,
loader: 'vue-loader'
}
]
}
};
elixir(mix => {
mix.sass('app.scss')
.webpack('app.js',null, null, config);
});
Now I am not getting any errors while compiling gulp webpack however when I try to view the page in the browser, it doesn't show the component and has an error/warn in the console
vue.js?3de6:513[Vue warn]: invalid template option:[object Object]
(found in component <test>)
vue.js?3de6:4085Uncaught TypeError: Cannot read property 'child' of null(…)
My app.js main entry file (default provided by Laravel)
require('./bootstrap');
Vue.component('test-component', require('./components/test-component'));
const app = new Vue({
//el: '#app',
}).$mount('#app');
What am I missing? Any pointers would be appreciated.
You have to use html-loader instead of vue-loader.
npm install html-loader --save-dev
Your gulpfile.js (need to change the loader):
const config = {
module: {
loaders:[{
test: /\.html$/,
loader: 'html'
}]
}
};
elixir((mix) => {
mix.sass('app.scss')
.webpack('app.js', null, null, config);
});
Your test-component.js (no changes, you're good to go):
export default {
template: require('./test-component.template.html')
}
Your test-component-template.html: (no changes, you're good to go)
<div class="test-component">
<h1>Test Component Goes Here</h1>
</div>
Important
Here's how to define your component:
Using Default
Vue.component('test-component', require('./test-component').default);
Or, simply use ES2015 import
import TestComponent from './test-component';
Vue.component('test-component', TestComponent);
Try this in your gulp file:
const elixir = require('laravel-elixir');
require('laravel-elixir-vue-2');
elixir(mix => {
mix.webpack('app.js');
});
And import your components like this:
import Test from './Components/Test.vue';
Vue.component('test', Test);

Categories

Resources