'React: add HTML from generic file path at server-side build time

The use case I'm trying to fulfill:

  1. Admin adds SVG along with new content in CMS, specifying in the CMS which svg goes with which content
  2. CMS commits change to git (Netlify CMS)
  3. Static site builds again
  4. SVG is added inline so that it can be styled and/or animated according to the component in which it occurs

Now - I can't figure out a clean way to add the SVG inline. My logic tells me - everything is available at build time (the svgs are in repo), so I should be able to simply inline the svgs. But I don't know how to generically tell React about an svg based on variables coming from the CMS content. I can import the svg directly using svgr/weback, but then I need to know the file name while coding, which I don't since it's coming from the CMS. I can load the svg using fs.readFileSync, but then the SVG gets lost when react executes client-side.

I added my current solution as an answer, but it's very hacky. Please tell me there's a better way to do this with react!



Solution 1:[1]

Here is my current solution, but it's randomly buggy in dev mode and doesn't seem to play well with next.js <Link /> prefetching (I still need to debug this):

I. Server-Side Rendering

  1. Read SVG file path from CMS data (Markdown files)
  2. Load SVG using fs.readFileSync()
  3. Sanitize and add the SVG in React

II. Client-Side Rendering

  1. Initial Get:/URL response contains the SVGs (ssr worked as intended)
  2. Read the SVGs out of the DOM using HTMLElement.outerHTML
  3. When React wants to render the SVG which it doesn't have, pass it the SVG from the DOM

Here is the code.


import reactParse from "html-react-parser";
import DOMPurify from "isomorphic-dompurify";
import * as fs from "fs";

const svgs = {}; // { urlPath: svgCode }

const createServerSide = (urlPath) => {
  let path = "./public" + urlPath;
  let svgCode = DOMPurify.sanitize(fs.readFileSync(path));
  // add id to find the SVG client-side
  // the Unique identifier is the filepath for the svg in the git repository
  svgCode = svgCode.replace("<svg", `<svg id="${urlPath}"`);
  svgs[urlPath] = svgCode;
};

const readClientSide = (urlPath) => {
  let svgElement = document.getElementById(urlPath);
  let svgCode = svgElement.outerHTML;
  svgs[urlPath] = svgCode;
};

const registerSVG = (urlPath) => {
  if (typeof window === "undefined") {
    createServerSide(urlPath);
  } else {
    readClientSide(urlPath);
  }
  return true;
};

const inlineSVGFromCMS = (urlPath) => {
  if (!svgs[urlPath]) {
    registerSVG(urlPath);
  }
  return reactParse(svgs[urlPath]);
};

export default inlineSVGFromCMS;

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Rafael Emshoff