Images won't load after build when using React with Electron - reactjs

I'm developing an app using React and Electron.
I'm storing the images in src/assets/images. When I run the app in development using react-scripts start and electron . everything works fine.
The problem occurs when the react app is built using react-scripts build. When the first view is loaded the path is correctly resolved, for example file:///D:/Projects/app-name/build/static/media/logo.e99ed458.png and the image is displayed.
Now, when the route changes, the image no longer works. In the network tab in devTools the request URL is file:///D:/main/static/media/logo.e99ed458.png which is obviously incorrect.
This is my component code:
import React from "react";
import Logo from '../../assets/images/ad.png';
const Logo = () => {
return (
<React.Fragment>
<img src={Logo} alt="no image" />
</React.Fragment >
);
};
export default Logo;
And in electron.js
mainWindow.loadURL(url.format({
protocol: 'file',
slashes: true,
pathname: require('path').join(__dirname, '../build/index.html')
}));
I've been trying to solve this problem for two days now. Does anyone know a solution?
EDIT:
I have also tried using PUBLIC_URL according to https://create-react-app.dev/docs/using-the-public-folder/
But the result is the same, the path is resolved correctly when the first view is displayed and after that it resolves to file:///D:/assets/images/logo.png.
When I log process.env.PUBLIC_URL it says that PUBLIC_URL is equal to ".".

I solved this issue by changing the routers instead of using BrowserRouter you should use HashRouter
This article https://www.freecodecamp.org/news/building-an-electron-application-with-create-react-app-97945861647c/ has a good example but Does not have the configurations of routers

Related

Getting "Unsafe attempt to load URL data:image/svg+xml..." in Safari using a through React component

I'm using rollup to bundle a react npm package that contains an icon component that takes a name as a prop and returns an Icon with that name wrapped by a react component.
This is the component code:
import sprite from './public/sprite.svg';
function Icon({ name }) {
return <svg className="svg-wrapper">
<use href={`${sprite}#${name}`} />
</svg>
);
}
The folder structure is the following:
- src
- - public
- - - sprite.svg
- - icons
- - - some-icon.svg
- - - some-other-icon.svg
- - index.tsx # component with the code mentioned above
And this is my rollup config:
export default {
plugins: [
esbuild({
sourceMap: false,
target: "esnext"
}),
image(),
svgicons({
inputFolder: "src/icons",
output: "public/sprite.svg"
}),
json()
]
}
This works fine in Chrome (although it does inline all the svg inside of the href which I think it's the purpose of this approach) but in Safari it triggers the following error:
Unsafe attempt to load URL data:image/svg+xml,%3c%3fxm ....
Domains, protocols and ports must match.
The thing is, as mentioned, this is an npm package that packages the icons as part of the js bundle (inlining) so there's not much control over how this component is served since this is handled by the browser caching (also one of the key points of using this approach). I'm quite familiar with CORS and I know that perhaps avoiding to use data:image/svg+xml uri links would fix this but would increase the complexity of the build steps of this package (needing to build the icons using svgr/svgo and then have some kind of lookup table to give back the right icon based on the name prop i.e.).
So, ultimately my question is, with the sprite approach in a react component library is there a foolproof way of avoiding these kind of issues and cross-browser inconsistencies?
Thanks in advance for any help provided.
I have been struggling with this issue for a while. I guess this is a bug on Safari throwing an error because is dealing with a dataURI as if it was an external URL.
About your code, you could expose your sprite in a public folder or publish it in a cdn (good for caching purposes) and change the way rollup is handling your svg (it seems it is packing your svg as a dataURI). Alternatively, I implemented a workaround to convert the dataURI in a blob.
import sprite from './public/sprite.svg';
function dataURItoBlobUrl(dataURI: string) {
const svg = decodeURI(dataURI).split(',')[1];
const blob = new Blob([svg], { type: "image/svg+xml" });
return URL.createObjectURL(blob);
}
const blobUrl = dataURItoBlobUrl(sprite);
export const Icon: FC<IconProps> = ({ name, ...props }) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" {...props}>
<use href={`${blobUrl}#${name}`}></use>
</svg>
);
};

vite react dynamic image url

hello guys i have problem with vite react dynamic images url not loading correctly and thats my component
<Flex>
{data?.map((tag) => (
<Box key={tag._id}>
<Image w="150px" h="150px" src={`http:localhost:5000/${tag.imageUrl}`}></Image>
</Box>
))}
</Flex>
when i check the network tab i see that the url of the client is added to the image url
http://localhost:3000/localhost:5000/uploads/1623642756947--Capture.PNG
in create react app maybe we can use reqiure() to fix that but i dont know how to fix that in vite and adding reqiure() throw an error
when i check the network tab i see that the url of the client is added to the image url
http://localhost:3000/localhost:5000/uploads/1623642756947--Capture.PNG
👉 This is because importing a static asset in vite return the resolved public URL when it is served.
This excerpt from the offical doc say (adapted according your question):
For example, http//:localhost:5000/${tag.imageUrl} will be /http:localhost:5000/uploads/1623642756947--Capture.PNG during development,
and become /assets/http:localhost:5000/uploads/1623642756947--Capture.PNG in the production build.
The solution is to use the server.proxy option of vite config to forward your dynamic assets requests to the desired server, in vite.config.js add:
server: {
proxy: {
// forward `/uploads/` endpoint to -> `http://localhost:5000/uploads/`
'^/uploads': {
target: 'http://localhost:5000/'
}
}
}
So in your component, Image should be declared like that :
<Image w="150px" h="150px" src={`${tag.imageUrl}`}></Image>
Now when you hit
http://localhost:3000/uploads/1623642756947--Capture.PNG
The asset (image) will be loaded from to http://localhost:5000/uploads/1623642756947--Capture.PNG
You can get more information about Static Assets Handling here and vite proxy options here.
Solution tested and working ✔
You can actually use the dynamic URL pattern via template literal. This is what it may look like for your case:
function getImgUrl(name) {
return new URL(`${name}`, import.meta.url).href
}
<Flex>
{data?.map((tag) => (
<Box key={tag._id}>
<Image w="150px" h="150px" src={ getImgUrl(tag.imageUrl) }></Image>
</Box>
))}
</Flex>

Full-fledged PDF.js viewer in React

I'm looking to use the full-featured PDF.js in a React component in a Next.js project, as seen in Firefox and as on this online demo. Some important features here are being able to navigate to a certain page number by typing it in, and searching for text in the PDF. Is there a React component available for that?
The library react-pdf is nice for rendering a single page, but doesn't provide a toolbar or a convenient way of lazily loading pages in a scrollable view.
Similar to the questions How to use full PDF.js viewer with toolbar in webpack and Vuejs? (where the accepted answer provides a Vue component) and Embed Full Mozilla pdf.js viewer in vue.js ( using webpack via vue-cli ), but for React.js.
I tried including including /web/viewer.html as part of the inner HTML of a React component by doing the following, but it didn't work out.
Download the latest release and extract it to a folder part of my Next.js project (which I called pdfjs). I tried several folders, such as /client, /client/components, /pages, /node_modules, and /.
Run npm install --save-dev html-loader
Use this Webpack loader that parses HTML files, by changing next.config.js to the following:
module.exports = {
// …
webpack: (config, options) => {
config.module.rules.push({
test: /\.html$/,
exclude: /node_modules/,
use: { loader: 'html-loader' }
});
return config;
},
}
Create a simple page under /pages as follows:
import React from 'react';
import PdfViewer from '../pdfjs/web/viewer.html'
export default function () {
return (
<div className="content" dangerouslySetInnerHTML={{ __html: PdfViewer }} />
);
};
After running next in terminal to start a dev server and navigating to that page in the browser, I get an error about the JavaScript heap running out of memory.
Even if my computer had enough memory, I'm not sure that this would actually result in the PDF rendering – not to mention the danger of using dangerouslySetInnerHTML. It looks like a better solution would probably be to have an actual React component rather than trying to embed an HTML file.
I think this might be more of what your after. I wrapped it in a component for you already but this is a document viewer which can view PDF documents with out much work.
import React,{ Component } from 'react';
import ReactDOM from 'react-dom';
class DocView extends React.Component{
constructor(props){
super(props);
}
render(){
var url = "https://docs.google.com/viewerng/viewer?url="+this.props.src+"&embedded=true";
return(
<iframe style={this.props.style} src={url}></iframe>
);
}
}
export default DocView;
CloudPDF offers a React PDF viewer. It is basically pdf.js but then pre-rendered on the server. This gives the possibility for lazy loading of large pdf files and still keeping performance. And by default has a nice layout for the viewer.
import CloudPdfViewer from '#openbook/cloudpdf-viewer';
export default function () {
return (
<CloudPdfViewer documentId="346467a6-fa61-43ad-b45a-d1fdc3da0007" width="100%" height="500px" />
);
};
Disclamer: I am working for CloudPDF and it is still a beta version.

Nextjs: Unable to load images from static folder

How can I load images in a component in Next.js? Do I have to build the project first? If yes, is there a way to load the images without building first? I cannot get this to work, no matter what I try.
from the docs:
Next.js can serve static files, like images, under a folder called
public in the root directory. Files inside public can then be
referenced by your code starting from the base URL (/).
So, first add an image to public/my-image.png and then you can reference it:
<img src="/my-image.png" />
I think next.js will have a watch on this directory so you won't need to restart your server every time you put something in there.
The static directory has been deprecated. Place files in public/static directory
Another way I find out Next Images
installation:
npm install --save next-images
or
yarn add next-images
Usage:
Create a next.config.js in your project
// next.config.js
const withImages = require('next-images')
module.exports = withImages()
Optionally you can add your custom Next.js configuration as parameter
// next.config.js
const withImages = require('next-images')
module.exports = withImages({
webpack(config, options) {
return config
}
})
And in your components or pages simply import your images:
export default () => <div>
<img src={require('./my-image.jpg')} />
</div>
or
import myImg from './my-image.jpg'
export default () => <div>
<img src={myImg} />
</div>
From Next.js v11 onwards, one can now directly import images without any additional config or dependencies. Official example (comment mine):
import Image from 'next/image'
import profilePic from '../public/me.png'
function Home() {
return (
<>
<h1>My Homepage</h1>
<Image src={profilePic} alt="Picture of the author" />
{/* <img src={profilePic.src} alt="Picture of the author" /> */}
<p>Welcome to my homepage!</p>
</>
)
}
export default Home
Docs: next/image
With Next 10+
To serve an optimized image:
import Image from 'next/image'
<Image src={'banner.jpg'} alt='Home Page' width={100} height={100} />
Place the image in the public folder. All the referenced images must be present in the public folder at the build time. Image hot deployment will not work for images that reside in the public folder.
You also can refer to cross-domain images with <Image> tag.
<Image src={'https://www.example.com/banner.jpg'} alt='Home Page' width={100} height={100} />
To allow cross-domain images, ensure to add the below entry to your next.config.js
module.exports = {
images: {
domains: ['www.example.com'],
},
}
what i like to do for directing to images is using environment variables. in next.js they are easily set in next.config.js file like below:
// next.config.js
module.exports = {
env: {
PUBLIC_URL: '/',
}
};
then you can direct to your publics path wherever it is by using process.env.PUBLIC_URL like below:
<img src={`${process.env.PUBLIC_URL}/my-image.jpg`} />
the advantages of using PUBLIC_URL environment variable over hard coding the path is that you can use another path for when file arrangements change (like in server). for then you could set conditionally which PUBLIC_URL value to use in production and development.
update
sometimes the problem of images used with next/Image not showing is bc of not setting the right layout value or it lacks width and height attributes when used with layout other than fill.
Using Image component of Next.js version 13 is a little bit different than than its previous versions. It's actually easier and you can use optimization features with less effort and work arounds. In this version :
you're not obligated to set domains in next.config.js.
you can either set the image its width and height or set to fill and handle its sizing with styles or classNames which means you can set max-height or max-width. so in that case that you don't know your image's width and height it's be shown properly.
as well as its previous versions you can use priority and ...
I will add here one obvious case, that is usually easily forgotten. It keeps appearing when one re-structure a site page and our IDE "silently fails" to update the paths of a related file/component or simply when one is more tired or distracted.
If you are using a page inside a folder
ex: mysiteDomain/pagefolder/page
You should be careful when using relative path.
Something like <img src="logo.png" /> should be changed it to <img src="../logo.png" /> since the compiled page will also be inside a folder pagefolder.
The path in the src attribute will be relative to the compiled page.
As an alternative, you could simply use an absolute path like for ex <img src="/logo.png" />. The path in the src attribute will be relative to the compiled root of the site.
Do NOT put public into /src!
In my case, I had a src dir into which I put my pages etc., which is an option described here. But I ALSO accidentally moved the public dir there. This will mess nextjs up -- you need to keep public in the root dir.
After running next build && next export and your images are not visible do this:
// next.config.js
/** #type {import('next').NextConfig} */
module.exports = {
reactStrictMode: true,
images: {
loader: "custom",
loaderFile: "./imageLoader.js",
},
assetPrefix: "./",
env: {
// dev does not need static path
ROOTDIR: process.env.NODE_ENV === "development" ? "" : "file:///C:/Users/.../out",
},
};
Create an imageLoader.js too in the root project
export default function imageLoader({ src, width, quality }) {
return `process.env.NODE_ENV === "development" ? "" : "file:///C:/Users/.../out${src}?w=${width}?q=${quality || 75}`;
}
Where file:///C:/Users/.../out refers to full path to the root of your build
Now you can append process.env.ROOT before "/*"

Webpack/react hot reload reloading whole page?

This is probably asked all the time, but I've tried every approach under the sun and can't find a solution.
I've created a repo to make it easier to get help. You can clone it, run npm install and then npm start:dev to see a quick local server on http://localhost:8080.
It works, and when I change a file (say, src/components/Note/Note.css) the app does reload. However, I want to only reload the component, not the whole page. I'm not sure what I'm doing wrong. Any help will be appreciated!
To debug issues like this, enable "Preserve log" in Chrome DevTools console settings to preserve console log across page refresh.
The error was:
Uncaught RangeError: Maximum call stack size exceeded
This was fixed once the following changes were made:
Remove new webpack.HotModuleReplacementPlugin() from plugins (as webpack-dev-server is started with --hot)
Also opt-out of babel transpiling ES6 modules by updating presets in .babelrc to ["react", ["env", { "modules": false }]].
"modules": false is to tell babel to not compile import/exports and let webpack handle it as described here and here (Check step 3.3.c).
For anyone using Next.js:
If you edit a file in pages/, the whole page will reload. Bummer, state is lost.
If you edit a file in components/, only the relevant module will re-load (i.e., hot reload as you expect).
My recommendation: keep the files in your pages/ folder simple.
Iterate your design/details in a component file in the components/ folder.
For example:
// pages/welcome.jsx
import WelcomePage from "../components/welcomePage";
export default function Welcome() {
return <WelcomePage />;
}
// components/welcomePage.jsx
import React from "react";
import Layout from "./layout";
import { useContext, useEffect, useState } from "react";
import PlayerContext from "../../context/playerContext";
export default function WelcomePage() {
const { songTitle } = useContext(PlayerContext);
return (
<Layout>
<div className="...">
<div className="...">
Welcome! Let's play {songTitle}.
</div>
</div>
</Layout>
);
}
Then you can go ahead and make small edits to components/welcomePage.jsx without losing state in your browser.

Resources