How to use next/image inside npm packege - reactjs

I have next.js wrapper project around react ui library, which is included as npm package. Is there a way to use next/image for images optimization inside this React library?
I know that import next/image inside react project is not possible. So what is the approach to optimize images with next/image if images are inside another package?
Please, help! Thanks!

Define Your own Image component and use it everywhere in your project, it might helpful!
import React from "react";
const Image = ({ src, ...props }) => {
try {
return React.createElement(require.resolve("next/image").default, {
src: typeof src === "string" ? src : src.src,
...props
});
} catch {
console.log("Not using Next.js");
return React.createElement("img", { src, ...props });
}
};
export default Image;
Use it like this (Please take this as an example)
import Image from "../utils/image";
import bg from "../assets/bg.jpg";
const Foo = () => <Image src={bg} height="640" width="959" />;
export default Foo;

Related

If possible, how to use MUI with Qwik framework?

I try Qwik framework which looks a lot like Reactjs and uses jsx. And suddenly, I wonder if Reactjs libraries such as MUI can work with Qwik framework.
I tried this code:
import { component$ } from "#builder.io/qwik";
import Add from "#mui/icons-material/Add";
import IconButton from "#mui/material/IconButton";
const AddToCartButton = component$(() => {
return (
<IconButton>
<Add />
</IconButton>
);
});
export default AddToCartButton;
But I got this this error:
QWIK ERROR Code(25): Invalid JSXNode type. It must be either a function or a string. Found: {
'$$typeof': Symbol(react.memo),
type: {
'$$typeof': Symbol(react.forward_ref),
render: [Function: Component] { displayName: 'AddIcon', muiName: 'SvgIcon' }
},
compare: null
} Error: Code(25): Invalid JSXNode type. It must be either a function or a string. Found:
at logError (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4515:58)
at logErrorAndStop (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4521:21)
at qError (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4585:16)
at Proxy.jsx (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:605:23)
at AddToCartButton_component_4S0nJgnxzBU (/src/addtocartbutton_component_4s0njgnxzbu.js:11:55)
at useInvoke (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:149:30)
at E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4676:32
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async renderSSR (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:5280:9)
at async Proxy.renderToStream (E:\qwik\flower\node_modules\#builder.io\qwik\server.cjs:582:3)
at async file:///E:/qwik/flower/node_modules/#builder.io/qwik/optimizer.mjs:1776:30
QWIK ERROR Code(25): Invalid JSXNode type. It must be either a function or a string. Found: Error: Code(25): Invalid JSXNode type. It must be either a function or a string. Found:
at logError (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4515:58)
at logErrorAndStop (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4521:21)
at qError (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4585:16)
at Proxy.jsx (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:605:23)
at AddToCartButton_component_4S0nJgnxzBU (/src/addtocartbutton_component_4s0njgnxzbu.js:11:55)
at useInvoke (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:149:30)
at E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:4676:32
at processTicksAndRejections (node:internal/process/task_queues:96:5)
at async renderSSR (E:\qwik\flower\node_modules\#builder.io\qwik\core.cjs:5280:9)
at async Proxy.renderToStream (E:\qwik\flower\node_modules\#builder.io\qwik\server.cjs:582:3)
at async file:///E:/qwik/flower/node_modules/#builder.io/qwik/optimizer.mjs:1776:30
not rendered
JSX in this case is the templating language of Qwik but the underlyings are different. It is made similar so you have an easier transition from react as stated in their docs.
Qwik is familiar for React developers and can be used to build any type of web site or application.
Qwik offers some adapter for react components you need to install and wrap your components in.
npm i -D #builder.io/qwik-react
And then the usage should look like the example in their repo.
/** #jsxImportSource react */
import { qwikify$ } from '#builder.io/qwik-react';
import { Button } from '#mui/material';
export const App = qwikify$(() => {
return (
<>
<Button variant="contained">Hola</Button>
</>
);
});
This thread is a bit older but maybe someone stumbles across it like me.
I had the same issue using a UI-component library and resolved it with the following steps.
adding qwikReact into the vite.config file:
import { defineConfig } from "vite";
import { qwikVite } from "#builder.io/qwik/optimizer";
import { qwikCity } from "#builder.io/qwik-city/vite";
import { qwikReact } from "#builder.io/qwik-react";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig(() => {
return {
plugins: [qwikCity(), qwikVite(), qwikReact(), tsconfigPaths()],
preview: {
headers: {
"Cache-Control": "public, max-age=600",
},
},
};
});
qwikify() must be used in a seperate file only with /** #jsxImportSource react */ as Jonathan pointed out.
Be aware that react components will not be treated the same way in Qwik. As stated in the docs it should be a migration/testing tool for existing projects where react components should be introduced in "Wide islands".
For those of you who are using Qwik Speak for I18N, the proposed solution will not work as is because Qwik-Speak won't be able to handle the JSX. The solution is to individually wrap the MUI component and then use it normally as so:
import { component$ } from "#builder.io/qwik";
import { Link } from "#builder.io/qwik-city";
import { $translate as t, Speak } from "qwik-speak";
import Button from "#mui/material/Button";
import { qwikify$ } from "#builder.io/qwik-react";
export const MUIButton = qwikify$(Button);
export default component$(() => {
return (
<Speak assets={["welcome"]}>
<div>
<h1>{t("welcome.title##Welcome")}</h1>
<MUIButton variant="contained">Do Something</MUIButton>
</div>
</Speak>
);
})

React testing library can't read styles using Tailwind CSS classes

I have a simple React component that will initially have a Tailwind CSS class of hidden which apply CSS display: none and will change the class to visible on button click.
When I test with expect().not.toBeVisible() it tells me the element is already visible while it has a hidden class.
If I don't use Tailwind CSS and use a normal style={{display: 'none'}} it'll correctly identify that the element isn't visible. That means clearly the issue is with Tailwind CSS.
Here's my test:
test("Notification bar should be initially hidden but visible on click", async () => {
render(<Notifications />);
expect(await screen.findByTestId("list")).not.toBeVisible();
// this test fails while the element already has a Tailwind CSS class of "hidden"
});
While this's my component:
<ul className="hidden" data-testid="list">
<li>item 1</li>
</ul>
The solution explained in this Stack Overflow: cannot check expectelm not tobevisible for semantic ui react component. Based on that thread, I extend the solution to make it works with TailwindCSS as the steps explained below,
Project structure
root/
src/
test/
index.css
test-utils.tsx
component.test.tsx
index.css
1. Generate CSS from the TailwindCSS template files
By issuing the command below, the CSS file called index.css will be generated in src/test directory
npx tailwindcss -i ./src/index.css -o ./src/test/index.css
Further reading: TailwindCSS installation
2. Create custom render function
Next we need to inject the generated CSS file into the JSDOM. Custom render function will be useful so we won't be needed to repeat this task for each test
import { render, RenderOptions } from '#testing-library/react';
import React, { FC, ReactElement } from 'react';
import fs from 'fs';
const wrapper: FC<{ children: React.ReactNode }> = ({ children }) => {
return <>{children}<>;
};
const customRender = (ui: ReactElement, options?: Omit<RenderOptions, 'wrapper'>) => {
const view = render(ui, { wrapper, ...options });
const style = document.createElement('style');
style.innerHTML = fs.readFileSync('src/test/index.css', 'utf8');
document.head.appendChild(style);
return view;
};
export * from '#testing-library/react';
export { customRender as render };
Further reading: Testing Library Setup
3. Perform testing, unit test suppose to be success now
import React from 'react';
import { render, screen } from './test-utils';
test('Renders hidden hello world', () => {
render(<span className="hidden">Hello World</span>);
expect(screen.getByText('Hello World')).not.toBeVisible();
});
Why souldn't we use toHaveClass matchers instead?
it wouldn't align with the Testing Library guiding principle of “emphasize a focus on tests that closely resemble how your web pages are interacted by the users“ because by doing so, you are interacting with the component unnaturally

Use SVGR and Material UI together

i'm trying to use SVGR to convert my svg into react components and i need to use <SvgIcon /> of Material UI and pass the converted component as a prop to it.
nothing wrong with this yet.
but,
SVGR saves these component in a folder called svgCom for example and inside of this folder there is index.js plus converted svg components.
i need to wrapp all these components inside of <SvgIcon> so i don't have to wrap this icon with <SvgIcon/> again for each use case;
so far i try to add this component in template.js of SVGR but it throw me an error when try to parse .
is this the best way of doing such thing or there is better way ?
if it is what's wrong with my template.js ?
here is my templete.js :
function template(
{ template },
opts,
{ imports, componentName, props, jsx, exports },
) {
return template.ast`
${imports}
import { SvgIcon } from "#material-ui/core";
///////////////////////////////////// error here
const ${componentName} = (${props}) => <SvgIcon component={${jsx}} />
${exports}
`
}
module.exports = template
thank you.
We had the same problem and after some research I came to the following solution:
const {
identifier,
jsxClosingElement,
jsxElement,
jsxIdentifier,
jsxOpeningElement,
jsxSpreadAttribute,
} = require('#babel/types')
const iconTemplate = ({ template }, _, { componentName, jsx, exports }) => {
const wrappedJsx = jsxElement(
jsxOpeningElement(jsxIdentifier('SvgIcon'), [jsxSpreadAttribute(identifier('props'))]),
jsxClosingElement(jsxIdentifier('SvgIcon')),
[jsx],
false
)
return template.ast`
import React from 'react'
import SvgIcon from '#material-ui/core/SvgIcon'
const ${componentName} = (props) => ${wrappedJsx}
${exports}
`
}
module.exports = iconTemplate
If you don't want {...props} on the <svg> tag, you have to disable the expandProps option

How to add Bootstrap JS and Jquery JS to Next JS

Here's an application running on create-react-app and Next JS
. Difference between them is CRA seems to have loaded bootstrap.mon.js and jquery.min.js while NextJS has not. I added a HEAD section to NextJS code through next/head and attempted to load both JS files. Although there were no errors, I did not see right results either.
Can someone help me understand why this happens with NextJS and what should I do to get NextJS load my application right with bootstrap and jquery
Add this snippet to your _app.js just above your return code
useEffect(() => {
import("bootstrap/dist/js/bootstrap");
}, []);
so your complete _app.js will be like this
import { useEffect } from 'react';
function MyApp({ Component, pageProps }: AppProps) {
useEffect(() => {
import("bootstrap/dist/js/bootstrap");
}, []);
return <Component {...pageProps} />
}
export default MyApp
You have to require the modules at the client-side. so you can use this
// _app.js
if (typeof window !== "undefined") {
require("jquery");
require("popper.js");
require("bootstrap/dist/js/bootstrap");
}
You can require client-side libraries in componentDidMount() after installed via npm.
import React, { Component } from 'react';
export class HomePage extends Component {
render() {
return (
<div>
Hello
</div>
);
}
componentDidMount() {
require('jquery');
require('popper');
require('bootstrap');
}
}
export default HomePage;

Using marked in react

I want to use marked in reactjs as described in the reactjs docs.
<div>{marked(mystring)}</div>
I use babel so I import marked like this:
import { marked } from 'marked';
Unfortunately the import statement does not work. marked is not defined.
How do I have to import marked here, so that I can use it?
Here's one way to use marked with React:
Ensure that you've installed marked
Include marked in your project's package.json file:
// package.json
{
dependencies: {
react: "^17.0.0",
marked: "^4.0.0",
},
}
Import marked in your .jsx (or related) file:
import { marked } from "marked";
Use the dangerouslySetInnerHTML approach as shown in the example below:
import React from "react";
import { marked } from "marked";
class MarkdownExample extends React.Component {
getMarkdownText() {
var rawMarkup = marked.parse("This is _Markdown_.");
return { __html: rawMarkup };
}
render() {
return <div dangerouslySetInnerHTML={this.getMarkdownText()} />;
}
}
The dangerouslySetInnerHTML attribute gives you the ability to work with raw (HTML) markup. Make sure to take care when using this attribute, though!
Alternative (Safe)
If you don't want to use dangerouslySetInnerHTML and safely render HTML. Try marked-react, which internally uses marked to render the html elements as react components
npm i marked-react
import Markdown from "marked-react";
const MarkdownComponent = () => {
return <Markdown>{rawmarkdown}</Markdown>;
};
Another alternative is react-markdown
Here is another way of using marked with React Hooks:
Create your MarkedConverter component
import { useState } from 'react'
import marked from 'marked'
export const MarkedConverter = () => {
const [markedVal, setMarkedVal] = useState(
'# Welcome to my React Markdown Previewer!'
)
return <div dangerouslySetInnerHTML={createMarkUp(markedVal)}></div>
}
Create Markup function and pass the value from MarkedConverter Component
export const createMarkUp = (val) => {
return { __html: marked(val) }
}
Finally you can import MarkedConverter Component to any of your Component
With the marked-wrapper react-marked-markdown:
import { MarkdownPreview } from 'react-marked-markdown'
export default ({ post }) => (
<div>
<h1>{ post.title }</h1>
<MarkdownPreview value={ post.content }/>
</div>
)
If you just want to import marked:
import marked from 'marked';
Then call the function in your component:
marked('# Markdown');
Here's an example on how to use marked with react:
Install marked with NPM : npm i marked
import it in your react app (this example is created with create-react-app), and using it
example of a react component using "marked"
result in the browser :
preview

Resources