Emotion.js with create-react-app: ReferenceError: jsx is not defined - reactjs

I am trying to use emotion.js with create-react-app and TypeScript.
Following the documentation I added #emotion/core, used import {jsx, css} from '#emotion/core'; and added /** #jsx jsx */ at the top of my file.
But when I am running the app, the following error gets thrown:
ReferenceError: jsx is not defined
This is my example component:
/** #jsx jsx */
import {jsx, css} from '#emotion/core';
import React from 'react';
import {NavigationBar} from "./NavigationBar";
export const Header: React.FunctionComponent<{}> = () => (
<div css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}>
<NavigationBar/>
Test
</div>
);
The line, the error gets thrown at is the definition of the function: export const Header: React.FunctionComponent<{}> = () => (
Any help how to get this to work (other than ejecting create-react-app scripts) would be much appreciated.

UPDATE: Emotion should work right out of the box now according to this comment on github.
The solution is to put a jsx; statement somewhere in the file, like so:
/** #jsx jsx */
import {jsx, css} from '#emotion/core';
import React from 'react';
import {NavigationBar} from "./NavigationBar";
jsx;
export const Header: React.FunctionComponent<{}> = () => (
<div css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}>
<NavigationBar/>
Test
</div>
);

To add to Johannes Klauß's answer, you don't need to reference jsx;
You just need the pragma statement (top-line and import):
/** #jsx jsx */
import { jsx } from "theme-ui"
Example:
/** #jsx jsx */
import {jsx, css} from '#emotion/core';
import React from 'react';
import {NavigationBar} from "./NavigationBar";
export const Header: React.FunctionComponent<{}> = () => (
<div css={{
backgroundColor: 'hotpink',
'&:hover': {
color: 'lightgreen'
}
}}>
<NavigationBar/>
Test
</div>
);
you may need to add:
/** #jsxRuntime classic */
as well if using theme-ui and next.JS

Related

React testing library not rendering Emotion CSS

Running React Testing Library to generate snapshots on JSX which uses the Emotion css prop results in no CSS being rendered.
I have tried using the "#emotion/jest/serializer" but still no luck.
Component:
<button
role="button"
css={(theme)=> {
backgroundColor: 'hotpink',
'&:hover': {
color: theme('lightgreen'),
},
}}
/>
Test:
import React from 'react';
import { render } from '#testing-library/react';
import { createSerializer } from '#emotion/jest';
import { Component } from './component';
expect.addSnapshotSerializer(createSerializer());
describe('IconComponent', () => {
it('should match the snapshot for the given props', () => {
const { asFragment } = render(<Component icon="knownIcon" />);
expect(asFragment()).toMatchSnapshot();
});
Snapshot:
(This gets rendered as an anonymous object rather than CSS)
exports[` 1`] = `
<DocumentFragment>
<button
css="[object Object]"
role="button"
/>
</DocumentFragment>
`;
I think you are just missing the final step.
https://emotion.sh/docs/css-prop
Set the jsx pragma at the top of your source file that uses the css prop. This option works best for testing out the css prop feature ...... such as Create React App 4 then /** #jsx jsx / pragma might not work and you should use /* #jsxImportSource #emotion/react */ instead.
From the emotion doc, adding /* #jsxImportSource #emotion/react */ on the top of your component file helps css option to render probably in the test.
CustomButton.js
/** #jsxImportSource #emotion/react */
export function CustomButton() {
return (
<button
css={{
"backgroundColor": "hotpink",
"&:hover": {
color: "lightgreen"
}
}}
></button>
);
}
Result
exports[`IconComponent should match the snapshot for the given props 1`] = `
<DocumentFragment>
.emotion-0 {
background-color: hotpink;
}
.emotion-0:hover {
color: lightgreen;
}
<button
class="emotion-0"
/>
</DocumentFragment>
`;
If you are not using create-react-app, use the follow instead:
/** #jsx jsx */
import { jsx } from '#emotion/react'
Here is the repo, you can clone it to test it.
Older Version
For older version of react (< 16.4), you will need to use back "#emotion/core" instead of "#emotion/react" to transpile the file in old way.
package.json
"#emotion/core": "10.1.1",
Button.js
/** #jsx jsx */
import { jsx } from '#emotion/core' <--- use the #emotion/core to transpile the file in old way.
import React from "react";
const Button = () => {
return (
<button
css={{
backgroundColor: "hotpink",
"&:hover": {
color: "lightgreen"
}
}}
></button>
);
};
export default Button;
Here is the repo for demonstration
Another solution https://emotion.sh/docs/testing#writing-a-test
import React from 'react'
import renderer from 'react-test-renderer'
const Button = props => (
<button
css={{
color: 'hotpink'
}}
{...props}
/>
)
test('Button renders correctly', () => {
expect(
renderer.create(<Button>This is hotpink.</Button>).toJSON()
).toMatchSnapshot()
})

Styles not applied at child component with Emotion 11 css prop in Gatsby and Typescript project

I'am using Emotion CSS prop to style my Gatsby / Typescript application.
I am using Gatsby 2 and Emotion 11.
In e.g. my Header component I render a Logo component:
/** #jsx jsx */
import { FC } from 'react';
import { jsx } from '#emotion/react';
import Logo from 'components/Logo';
import useSiteMetaData from 'hooks/useSiteMetadata';
import Styles from './Header.styles';
import { Props } from './Header.types';
const Header: FC<Props> = () => {
const styles = Styles();
const { title } = useSiteMetaData();
return (
<header css={styles.root}>
<Logo siteTitle={title} css={styles.logo} />
</header>
);
};
export default Header;
But on the css prop of my child component I get a Typescript error:
Property 'css' does not exist on type 'IntrinsicAttributes &
Props'.ts(2322)
I added below to tsconfig.json and that solves the linting error:
{
"compilerOptions": {
"types": ["#emotion/react/types/css-prop"],
}
}
But somehow the styles are not applied from within my Header.styles.ts, so I am able to overwrite/add some styles of my child component (Logo)?
You have such example in css-prop docs.
The P component acts like your Logo component, and ArticleText is your Header
const P = props => (
<p
css={{
margin: 0,
fontSize: 12,
lineHeight: '1.5',
fontFamily: 'sans-serif',
color: 'black'
}}
{...props} // <- props contains the `className` prop
/>
)
const ArticleText = props => (
<P
css={{
fontSize: 14,
fontFamily: 'Georgia, serif',
color: 'darkgray'
}}
{...props} // <- props contains the `className` prop
/>
)
You mean need to pass the className prop, see related answer.

How to use a SVG React component as background?

I have SVG files written in Function Components.
import React from "react";
const MySVGBackground= (props) => {
return (
<svg> some svg here</svg>
);
};
export default MySVGBackground;
I need to render this as a background:
<div style={{ backgroundImage: `url(${MySVGBackground})` }}> </div>
but it's not working.
I can't purely import the SVG directly, because it gives me an error of Unexpected Token. So I have to wrap the SVG into the FC and export it.
Here is the sample code:
https://codesandbox.io/s/react-background-issue-9wt4x?file=/src/App.js
Try this:
https://codesandbox.io/s/react-background-issue-ut933
import React from "react";
import "./styles.css";
import BackgroundSVG from "./backgroundSVG";
// import this to render static markup
import { renderToStaticMarkup } from 'react-dom/server';
export default function App() {
// convert component to string useable in data-uri
const svgString = encodeURIComponent(renderToStaticMarkup(<BackgroundSVG />));
return (
<div className="App">
<h2
style={{
backgroundImage: `url("data:image/svg+xml,${svgString}")`
}}
>
Svg background
</h2>
</div>
);
}
The data: image/svg+xml;utf8 part tells the browser that raw image data is following.
More information can be found here:
https://css-tricks.com/lodge/svg/09-svg-data-uris/
https://gist.github.com/iansinnott/2e8fe9d9e4c6c7c55793d38f81c999a3

Material-UI withStyles not adding classes to props

I'm trying to implement some styling using Material-UI's withStyles method, however I'm not able to get classes as a prop. Any suggestions as to what I'm missing? I've included the relevant code below, but note that there is an <App> component in this file that I'm leaving out for brevity.
import React from 'react'
import ReactDOM from "react-dom";
import {Paper, Typography} from '#material-ui/core'
import {withStyles} from '#material-ui/core/styles'
import NavBar from "./navBar";
class Recipe extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('Recipe Did Mount')
}
render() {
const {recipeData, classes} = this.props;
return (
<Paper>
<Typography className={classes.recipeName}>{recipeData.name}</Typography>
<Typography className={classes.recipeIngredients}>{recipeData.ingredients}</Typography>
<Typography className={classes.recipeInstructions}>{recipeData.instructions}</Typography>
</Paper>
)
}
}
const styles = {
root: {
fontSize: "1.0rem",
margin: "0px"
},
recipeName: {
fontSize: "1.0rem",
margin: "0px"
},
recipeIngredients: {
fontSize: "1.0rem",
margin: "0px" },
recipeInstructions: {
fontSize: "1.0rem",
margin: "0px" }
};
withStyles(styles)(Recipe);
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<App/>,
document.body.appendChild(document.createElement('div')),
)
});
Since you aren't setting withStyles(styles)(Recipe); into a variable, I suspect you must be using Recipe directly within App.
withStyles doesn't change Recipe. withStyles creates a new component that wraps Recipe and passes the classes prop to it. In order to see the classes prop, you need to use the newly-created component with something like the following:
const StyledRecipe = withStyles(styles)(Recipe);
const App = ()=> {
return <StyledRecipe/>;
}
Assuming App is defined in a separate file (for others who may come looking for this question), change the
`withStyles(styles)(Recipe);`
To
export default withStyles(styles)(Recipe);
As Ryan already explained ' withStyles is the higher order component that creates and returns a new component'

React createElement type is invalid

I am having trouble trying to publish a node module, material-ui-next-datepicker
It is working locally but not when installed as a node module
import * as React from 'react'
import * as ReactDOM from 'react-dom'
import {MuiThemeProvider, createMuiTheme, withStyles, StyledComponentProps, Theme} from 'material-ui/styles'
import DateFormatInput from 'material-ui-next-datepicker'
const theme = createMuiTheme()
const styles = (theme:Theme):Record<string, React.CSSProperties> => ({
container: {
width: '100vw',
height: '100vh',
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}
})
#(withStyles as any)(styles)
class DemoPage extends React.Component<DemoPageProps, DemoPageState> {
constructor(props) {
super(props)
this.state = {
date: undefined
}
}
onChange = (date:Date) => {
console.log(date)
this.setState({date})
}
render() {
const {classes} = this.props
const {date} = this.state
return (
<div className={classes.container}>
<DateFormatInput name='date-input' value={date} onChange={this.onChange} label='Date'/>
</div>
)
}
}
interface DemoPageProps extends React.Props<{}>, StyledComponentProps {
}
interface DemoPageState {
date: Date
}
ReactDOM.render(
<MuiThemeProvider theme={theme}>
<DemoPage/>
</MuiThemeProvider>
, document.getElementById('root'))
Here's the error in the browser
When I do print out DateFormatInput, it looks fine...definitely not undefined
I am not too familiar with dependencies and how to differentiate between peer, ambient or bundled. So, I do need some help in making this node module work
Finally able to solve this issue simply by adding .npmignore
I removed all my demo files like:
demo/
src/
index.html
tsconfig.json
webpack.config.js
Then it starts working fine by importing...

Resources