Embed SVGs in JSX at build time? - reactjs

I'm relatively new to React, using create-react-app to build simple prototypes. I'd like to be able to export SVGs from Sketch or a similar design tool and use them in my React app, embedded directly in the JSX since that allows styling via CSS.
This is relatively straightforward with a little effort: copy the exported SVG code out of the original .svg file, tweak how the xmlns attributes are named, done.
But if I have a lot of images and am iterating on them frequently, that becomes incredibly high-effort. I'd like a way to take a directory of SVGs at build time and basically concatenate them into a single .js file, e.g.,
const icons = {
first: <svg>...contents of first svg file...</svg>,
second <svg>...contents of second file...</svg>,
...
}
This is relatively trivial to do via shell script or something, but it would be great to integrate into the build process. How would I go about doing something like this?
Thanks!

As of create-react-app version 2, you can import svg files directly as React components. As you're just starting with development and have probably installed create-react-app package recently and won't suffer any possible side-effects from updating to new version of create-react-app, just edit the react-scripts entry in package.json to say "react-scripts": "^2.0.3" and run npm install or yarn install.
Then you'll be able to do this:
import { ReactComponent as Logo } from './logo.svg';
const App = () => (
<div>
<Logo />
</div>
);
Source: create-react-app Github page readme

Related

Share i18next translations between nextjs and component workspaces

We have a yarn 2 monorepo setup with the following workspaces:
/root
/app (nextjs)
/components (individual react functional components)
/storybook
/constants
Currently, /app has the i18next translation files stored local to the workspace, but I would like to move that into the constants workspace so that all workspaces can share the same translations. I had no issue moving the translations there and loading them in both the /app and /storybook workspaces.
Also, currently, all translations happen only in /app. The /components workspace has no translations, and the translated text is passed in props to the dumb components. So, a component in /app looks something like this:
import ListBox from "#root/components/ListBox";
import {useTranslation} from "react-i18next";
export default const Page() {
const [t] = useTranslation();
const label = t("listBoxLabel"); // <-- exists in en.json as a key
return (
<div>
<ListBox label={label} />
</div>
)
}
As such, storybook also has to provide the "label" prop to render the component in stories, doing this in pretty much the same manner.
What I would like to do is instead translate the text at the /components workspace level, so that the translations can be done in one place, where it's used, to reduce prop drilling and also simplify things.
Something like this:
/components/ListBox.js:
import {useTranslation} from "react-i18next";
export default const ListBox() {
const [t] = useTranslation();
const label = t("listBoxLabel"); // <-- exists in en.json as a key
return <p>{label}</p>
}
When I run this code, it just prints out the translation key "listBoxLabel", in both storybook and the nextjs app.
I was able to fix storybook by wrapping stories with I18nextProvider and changing the dependencies in the /components workspace for i18next and react-i18next to move them from a normal dependency to a peer dependency, and storybook properly renders the translation. However, then the NextJS /app throws an error that the react-i18next Module Not Found when it imports the /components/ListBox.js code. It seems to require the /components workspace keep a normal dependency for that, but doesn't translate it in that case. If I remove the /components dependencies altogether, /storybook won't compile and start for the same reason.
Duplicating this exact code in the app or storybook, however, displays the correct translation. So, it has something to do with importing it from a sibling workspace in the monorepo, and I'm not sure exactly how to go about fixing that for the nextjs app.
Just a note: I don't use the next-i18next component. Instead, I am simply using react-i18next directly, without any providers configured. I did attempt to wrap the app with the I18nextProvider component similar to the storybook stories, but it didn't work.
Does anyone have any ideas where to look further or what to try for this? I would think loading a component from a workspace component library where the translations happen within the library would be a common scenario, but I haven't been able to find anyone else running into this issue.
I don't know exactly what's causing this, but it appears to be a nextjs, webpack, or yarn bug. I discovered the problem was related to this in my tsconfig.js file in the #root/app workspace:
{
"compilerOptions": {
"paths": {
"#root/components/*": ["../components/*"]
}
}
}
I was able to fix this by removing this setting, but it broke other code (some components were typescript), so I just explicitly declared those component modules in the custom.d.ts file as modules.
Where the next/yarn bug comes in: after I removed this line, and restarted the dev server, the i18n translations were still broken. Until I went and touched the components/ListBox.js file. This caused something to rebuild within next, and the translations showed up.
The ListBox.js file is not typescript, so I don't understand why this causes a problem.
While trying to resolve this issue, yarn kept getting into a bad state, next was telling me I didn't have typescript installed when I did, and multiple other tedious random errors...what a mess.

How to import a folder of svg files into react as ReactComponents

I am making a real-time card game using react for my front-end (which was made using CRA), and I am storing all of my cards as .svg files in a subfolder in the src folder. I have seen in other questions that for single files you could use import {ReactComponent as Image} from 'whatever.svg' and for an entire folder to use require.context(). While the import statement works, the require.context method just provides me with /static/media/whatever.randomStuff.svg files that I can only import by making an http(s) request to that location on the site. I have tried using #svgr/webpack in the require.context function, but even when installed manually, it gives me the same output. Is there a way for me to use the import statement or something else to import all of the .svg files in that folder as ReactComponents in the file instead of having to make an http(s) request?
The answer I was looking for was in this answer from another question. One thing I didn't know at the time of posting was that the version of webpack that react-scripts was using (v4.44.2) already had #svgr/webpack, so manually installing it with npm was essentially useless because it was probably already using it to generate the /static/media/fileName.randomStuff.svg files in the first place. In the end I used an intermediate file in the svg folder and did this:
interface CardCache {
[key: string]: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
}
const cache: CardCache = {};
/* eslint import/no-webpack-loader-syntax: off */
const files = require.context("!svg-react-loader?name=card!./", true, /\.svg$/);
files.keys().forEach(path => {cache[path] = files(path)});
export {cache};
I used the eslint comment because I did not want to replace react-scripts with something like react-app-rewired just so I could have access to the webpack.config.json file.

Typescript cannot load SVG as react components

I'm trying to import in Typescript some SVG icons, but I'm facing some problems.
At the first time I tried to import them, Typescript wasn't able to recognize the file extension.
I solved this issue by creating, as suggested in other Stack Overflow and Github topics, a custom.d.ts file with this rule inside:
declare module "*.svg" {
const content: React.StatelessComponent<React.SVGAttributes<SVGElement>>;
export default content;
}
But the problems seem to not finish here, even if the compilation seems going fine.
The current project I'm working on, is structured this way:
Typescript + React package (with SVG icons files) (SDK)
React Internal Sample page (package) to use the SDK
other internal packages...
For our development phase, we build through Webpack all the packages through different loaders and see the result through the Sample page.
But the final product flow to production is quite different: I export the SDK as CommonJS to an internal NPM Registry so another company can use it in a React project (the equivalent of the Sample page but for production) and push to production the final Webpack bundles with both projects inside.
So, to load in the Sample application the SVG icons, I'm using #svgr/webpack loader, which converts the files.
But when I have to export the SDK through npx tsc, I see that the exported folder, does not contain the folders with svg files.
I've tried to include them in tsconfig.json/files, but got this error:
TS6054: File '<path>/*.svg' has an unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'.
So, to attempt exporting them I converted my exporting script to use #svgr/cli to export the files to React files from SVGs before compiling to typescript:
// package.json
scripts: {
"build-ts": "rm -rf ./lib; yarn convert-svg-to-react; npx tsc",
"convert-svg-to-react": "npx #svgr/cli -d src src --typescript",
}
In this way, I get the new Typescript files mixed with the SVGs inside the package (so I'll have to remove them later) and I can see them in the exported folder lib.
But watching inside the Typescript exported code, I can see this line (for each svg import):
var close_svg_1 = __importDefault(require("./icons/close.svg"));
Leaving out the Typescript function for Babel __importDefault, you can see that it still requires the file svg, but what I have at this point, are the React components that replaces those files.
During development it works fine because #svgr/webpack loader, resolves the svg files.
But requiring svg files that do not exist, should make the application above it crash.
So, I'm stuck and I need some clues to get out of this situation.
Some clues that I got (but wasn't able to find how to do that), were:
[Best] Find how I can export raw svg files as they are during Typescript compilation without doing that manually, as they are not all in one folder but divided per components areas in the package tree. Doing this, I would tell the other company to add #svgr/webpack to its own building process.
Find how can I tell Typescript to import svg files without specify the extension (currently, removing .svg probably makes it fallback to .ts/tsx and therefore it cannot find the file with that name). In this way, the require would keep requiring the same file name but I could convert SVG to React Components without occurring in problems. But this would also require Typescript to export the file
Otherwise, I should convert all the SVGs in React components and directly use them instead of making them being compiled by #svgr/webpack, but I'm not sure this would have some other side-effects.
Any other clues or any way to achieve the ideas I got? Thank you everybody.

Debugging styled components with Create React App

I've installed Styled Components into my Create React App, and everything works fine, but by default, it looks as though the class name it appends to the element isn't based off of the styled component name (ie. MyButton should create an element with the class MyButton-134as23f).
In the Styled Components documentation, it says to install the babel-plugin-styled-components, and then configure the .babelrc file, however, from what I understand, we don't have access to that file until we eject from the app.
So how can I debug styled components while I am developing an app within Create React App?
I was able to find an answer to this:
Because Create React App is a zero-config application, the only way to add anything to the .babelrc file is to eject from React.
Obviously, I wanted to keep all of my tooling, and came across babel-plugin-macro. It's essentially a way for users to run libraries at compile time, without having to configure their Babel file beforehand.
So after installing it to my devDependencies, I then changed the import path to import styled from 'styled-components/macro, and all of the Babel plugin features that you would normally need to eject for came standard with Styled Components.
Let me know if you have any questions or trouble with my answer.
Hope this helps!

How to use the #salesforce/design-system-react package in my Create-React-App

I have an ejected React app that is based on Create-React-App, and I am trying to install the #salesforce/design-system-react package to use the Salesforce lightning components in it. But to use this package is not as easy (seems that I need some extra configuration for Barbel and Webpeck). I don't have much experience on config Barbel and Webpeck and need some help to get me started.
Can someone please let me know how can I get that .BABELRC and the Webpack v1 files described from this site: https://react.lightningdesignsystem.com/getting-started/ ?
Many thanks,
No need to configure anything. Just import the CSS.
In your index.js, add the following line.
import "#salesforce-ux/design-system/assets/styles/salesforce-lightning-design-system.min.css";
Now you should remove the styling in App.css since the default styling in Create React App will affect your Lightning Components (font-size for example)

Resources