hybridsjs / hybrids

Extraordinary JavaScript UI framework with unique declarative and functional architecture
https://hybrids.js.org
MIT License
3.05k stars 86 forks source link

svg is not interpreting the file #133

Closed ericktucto closed 3 years ago

ericktucto commented 3 years ago

I create this issue as documentation, to find a way to import svg files and render them in the component

I'm importing svg file but it is not being interpreted correctly

imagen

I am leaving webpack to do the import, I leave my configuration

const path = require('path')
const WebpackBuildNotifier = require('webpack-build-notifier')
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin')  // to minify svg files

module.exports = {
  entry: path.join(__dirname, 'src', 'index.js'),
  output: {
    path: path.join(__dirname, 'public', 'js'),
    filename: 'main.js'
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /(node_modules)/,
        loader: 'babel-loader'
      },
      {
        test: /\.sass$/,
        exclude: /(node_modules)/,
        use: ['raw-loader', 'sass-loader']
      },
      {
        test: /\.svg$/,
        exclude: /(node_modules)/,
        loader: 'raw-loader'
      }
    ]
  },
  plugins: [
    new WebpackBuildNotifier({
      title: 'All files were compiled'
    }),
    new ImageMinimizerPlugin({
      minimizerOptions: {
        plugins: [
          [
            'imagemin-svgo',
            {
              plugins: [{ removeViewBox: true, removeXMLNS: true }]
            }
          ]
        ]
      }
    })
  ]
}

To import svg files I use raw-loader then I minify it with image-minimizer-webpack-plugin (the latter uses imagemin-svgo), however, it is not rendered and it is seen in text

I'll let you know if I find a way to import svg files

smalluban commented 3 years ago

There are two things that you must follow:

  1. If you want to put RAW html/SVG content into the template, you must use innerHTML property of the wrapper element.
  2. SVG content is actually a HTML content, because it has root <svg> element from HTML space. the svg() function should only be used for the internal part of the SVG (if you create it in-place), as you can read in docs - https://hybrids.js.org/#/template-engine/overview?id=svg.

In your case, if your import contains a RAW content of the SVG file, you can use it like this:

import MyIcon from "../icons/my-icon.svg";

const MyElement = {
  render: () => html`
    <span innerHTML="${MyIcon}"></span>
  `,
};

However, you must know that passing innerHTML might open an XSS attack, so use it carefully (so you know what you pass there).

ericktucto commented 3 years ago

Thank you very much, the svg is showing in the DOM, it is enclosed in a span tag, this makes it easy for me to change the fill and size

If there is danger due to XSS attack, is there a way to import svg files? Importing svg files would be very useful, especially if you want to modify the svg, but simply show it in the component

smalluban commented 3 years ago

As long as you're in control over the input, everything will be fine. The XSS attack could occur if you would use a dynamic property as a content for innerHTML. Then, it is possible that in some cases, the user may set there unexpected content.

ericktucto commented 3 years ago

Thank you very much for the help, I had a complication with Chrome, the svg was not shown, because it did not have a height, I leave the code that helped me

import left from './arrow-left.svg';

const chrome = svg => {
  const $div = document.createElement('div');
  $div.innerHTML = svg;
  $div.firstChild.setAttribute('height', 20);
  return $div.innerHTML;
}

const Paginator = {
  render: () => html`<span innerHTML="${chrome(left)}"></span>`
}

I will close the issue because it was solved, I will always keep in mind to control the innerHTML to avoid XSS attacks