Thanks for the great work.
I'm trying to get this loader to work on server side as well. Since it's touches document directly, I guess getting it to work on the server is currently not possible. Can you please confirm?
If that is the case, do you have any plans of making it to work on the server as well?
@oliverox you can implement your own sprite behaviour via spriteModule option. Something like this:
webpack.config.js
...
module: {
loaders: [
{
test: /\.svg/,
// define path to custom sprite module
loader: 'svg-sprite?spriteModule=' + path.resolve(__dirname, 'server-side-rendering-sprite-module.js')
}
]
}
server-side-rendering-sprite-module.js
// simple sprite implementation which renders into string
function Sprite() {
this.symbols = [];
}
Sprite.prototype.add = function(image) {
this.symbols.push(image);
};
Sprite.styles = ['position:absolute', 'width:0', 'height:0', 'visibility:hidden'];
Sprite.prototype.render = function() {
return [
'<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="'+ Sprite.styles.join(';') +'">',
'<defs>',
this.symbols.join(''),
'</defs>',
'</svg>'
].join('');
};
module.exports = new Sprite();
my-app.js
require('./image.svg');
var sprite = require('./server-side-rendering-sprite-module');
var renderedSprite = sprite.render();
console.log(renderedSprite);
@oliverox does it helped?
@kisenka Thanks for the help in the right direction. I have not had enough time to implement it yet as I've been moving over the new year. Will try it soon. Thanks again.
@kisenka I stumbled upon same problem. Could you please elaborate on process of implementing the server-sider rendering module for React app?
My node app generates sprite and inlines it into html (this works well):
app.use((req, res, next) => {
const html = renderToString(
<Root />
);
let renderedSprite = Root.sprite;
res.send(template({ html, env, renderedSprite }));
});
and my components looks like this
import svg_aws from './img/aws.svg';
class Icon extends React.Component {
render() {
let { glyph } = this.props;
return (
<svg
className="icon"
width="81px"
height="81px"
dangerouslySetInnerHTML={{__html: '<use xlink:href="' + glyph + '"></use>'}}
/>
)
}
}
<Icon glyph={svg_react} />
The problem is that when webpack compiles it (with target: node) the svg_aws is undefined. So to my understanding the problem boils down to getting the sprite xlink:href at compile time. Any thoughts on this?
Well.. i came up with crappy solution actually:
const sprite = require('../../utils/svg-server-rendering.js');
...
let ids = sprite.symbols.map(i => {
return i.match(/id="(.*?)"/g)
});
let arr = ids.map(j => j[0].replace('id=', '#').replace(/\"/g, ""))
arr.map(el => <Icon glyph={el} />)
Pretty sure that it is not only ugly, but also wrong somewhere 馃槓
aaaand it does not work on webpack dev server
import svg_aws from './img/aws.svg';
<Icon glyph={svg_react} />
svg_aws
svg_react
Maybe wrong var reference?
No, i map all of them and they all undefined when render on server. Also initial version based on docs worked perfectly on development server (without SSR).
@olegakbarov sorry, I am vacation till 21 March so I can't help you now
No problem, i'll write up if got some results on this.
Also struggling with server-side rendering. Initially, the error I got was a type error because React is undefined. Found out that it seems it's the babel-loader trying to do its job (there is no webpack when running with node -r babel-register). I then tried to just get the correct id generated by node, assuming the sprite would already be setup thanks to the webpack build, but I don't get any svg file in my build output anyway and I'm now very confused.
I believe for SSR, we need a node require hook that will generate the correct ID for a given path, then it should all work fine since the sprite will be loaded client-side.
I would give it a go, but I'm first trying to get a sprite generated at all.
Sorry for delay. I am working on it
@kisenka do you still intend to work on this?
I've created the testcase https://github.com/kisenka/svg-sprite-loader-ssr-testcase for server-side rendering.
You will see that output from sprite loader which contains the whole sprite.
You can add your testcases in you forks and tell me.
Shouldn't something like SSR live in the default sprite impl? The very least it should do on a node environment it just return the symbol id (without using document) instead of failing.
@danilobuerger you're right. Sprite loader firstly created for in browser usage, so it works with browser environment out of the box. I think it will be good if sprite loader will use environment specific sprite implementation which depends on target webpack config option. Thoughts?
Hmm, so what we need to consider is client-only and server+client (render server side, pick up on client side) and server-only setups. Probably need to also consider outputing to a svg file (see #23).
Overall it might make more sense splitting this up in multiple loaders. One that returns symbol ids (or external ref + symbol ids) and builds the svg map. Another loader to include that svg map either as file or load it in dom or output it in some other way.
If _inline_, return symbol id on require. Include svg map in dom, updateable (?).
If _file_, return external ref + symbol id on require. Chunk svg map, maybe extractable with extract-text-webpack-plugin.
If _inline_, return symbol id on require. Output svg map so it can be rendered.
If _file_, return external ref + symbol id on require. Chunk svg map, maybe extractable with extract-text-webpack-plugin.
If _inline_, return symbol id on require. Output svg map so it can be rendered. Client should not include svg map in dom as its already present. Maybe it can updated it instead like react-helmet does for example.
If _file_, return external ref + symbol id on require. Chunk svg map, maybe extractable with extract-text-webpack-plugin. Client should not include svg map in dom.
Just as a heads-up, for my project, I switched to making a webpack bundle for the server as well (as per this article: http://jlongster.com/Backend-Apps-with-Webpack--Part-I) to avoid needing duplicate logic for every loader that node should know about.
Hi everyone! I've run into a similar issue, however I didn't want to webpack my entire backend so I came up with a kinda hacky solution.
I was already using ignore-styles as I import relevant stylesheets into my react components, so in order to generate the correct server side markup I just needed to add the following to my bootstrapping file:
const path = require('path');
const crypto = require('crypto');
require('ignore-styles').default(['.scss', '.css', '.svg'], (module, filename) => {
const base = path.basename(filename);
if (base.indexOf('.svg') > 0) {
module.exports = '#' + crypto.createHash('md5').update(filename).digest("hex");
}
});
Note this works with the following webpack config:
{
...
module: {
loaders: [
{
test: /\.svg$/,
loader: 'svg-sprite?' + JSON.stringify({
name: '[pathhash]',
prefixize: true
})
}
]
},
...
}
I hope this helps somebody! 馃槂
I can confirm that the approach provided by @kisenka in https://github.com/kisenka/svg-sprite-loader/issues/19#issuecomment-168149953 works for me.
I had to make the webpack config for the ?spriteModule=server-side-rendering-sprite-module conditional on the prerender build, and then in my app I just needed to import the server-side-rendering-sprite-module module and put the result of sprite.render() in the outgoing HTML.
However, I think that the approach outlined in #45 will be more efficient and easier to set up.
Confirming @hyatt03's solution works well! Thanks for sharing.
Hello!!
Are there any updates on this? (as I see this to be a feature for 2.0).
Also, could you, given you have the chance, point me in the right direction to use with React? Though I did check the example, I can't really see how to implement it with React.
Thanks in advance!
@lrojas94 work in progress, I planning to release alpha version on the next week.
Also, could you, given you have the chance, point me in the right direction to use with React?
Do you have concrete question about loader with React?
Thing is I'm using a server side rendered Application, which is why this loader is failing when going through the app initialization. In the meantime, I managed to make it work by only requiring the plugin if typeof window !== 'undefined', it's a temporal fix, but works, so will be looking forward to the alpha version next week :)
Thanks for taking the time to look out my problem here :+1:
@oliverox
@olegakbarov
@qur2
@danilobuerger
@hyatt03
@wmertens
@elado
@lrojas94
Hey guys [email protected] with server side rendering is on the way, please read about it here #91 and take part in discussion/voting. Thanks!
Check 2.0.1 release
Most helpful comment
Hi everyone! I've run into a similar issue, however I didn't want to webpack my entire backend so I came up with a kinda hacky solution.
I was already using ignore-styles as I import relevant stylesheets into my react components, so in order to generate the correct server side markup I just needed to add the following to my bootstrapping file:
Note this works with the following webpack config:
I hope this helps somebody! 馃槂