I am new to react-native.My application currently uses redux,react-redux,router flux & navigator.
The back end i need to work with is GraphQL. What should i do now?
Can i integrate Relay to my app without affecting anything related to redux or should i dump redux and use relay?. What about lokka? Really confused!! Can someone help me with code examples or anything related to this issue?
Thanks in Advance :)
I use relay and redux in same application without much(I dont have any till today) issues(the App will be in production after few weeks). I could explain how I achieved it. (I am also new react-native and Js development, I don't claim this as the best approach, but at least it works for me as I intended)
Setting up of relay and graphQL almost took a day of effort. For this use following commands:-
npm install babel-core --save-dev
npm install babel-preset-react-native --save-dev
npm install babel-relay-plugin --save-dev
npm install react-relay --save
npm install graphql --save-dev
npm install sync-request --save-dev
then create a file named babelRelayPlugin.js and copy the below code.
const getBabelRelayPlugin = require('babel-relay-plugin')
const introspectionQuery = require('graphql/utilities').introspectionQuery
const request = require('sync-request')
const url = 'your_api_here'
const response = request('POST', url, {
qs: {
query: introspectionQuery
}
})
const schema = JSON.parse(response.body.toString('utf-8'))
module.exports = { plugins: [getBabelRelayPlugin(schema.data, { abortOnError: true })] }
and replace the code your .babelrc with this:-
{
"passPerPreset": true,
"presets": [
"./scripts/babelRelayPlugin",
"react-native"
]
}
following classes may need to use this import statement:-
import Relay, {
Route,
DefaultNetworkLayer
} from 'react-relay'
And my App.js file look like:-
function configureStore(initialState){
const enhancer = compose(applyMiddleware(
thunkMiddleware,
loggerMiddleware
),
autoRehydrate()
);
return createStore(reducer,initialState,enhancer);
}
const store = configureStore({});
persistStore(store, {storage: AsyncStorage})
////relay network layer injecting
Relay.injectNetworkLayer(new DefaultNetworkLayer('your_api'))
export default class App extends Component {
render() {
return (
<Provider store={store}>
{//here is your react-native-router-flux Navigation router}
<NavigationRouter/>
</Provider>
);
}
}
After injecting relay network layer, you could use the following code in any containers to call from relay. Here is an example render method of one of my containers:-
render() {
var value = 'some_value';
return (
<View style={{flex:1,justifyContent:'center',alignItems:'center'}}>
<Relay.RootContainer
Component={TestComponent}
//relay_route is imported from relay_route.js
route={new relay_route({id:value})}
renderFetched={(data)=> {
return (
<TestComponent parentProps={this.props} {...data} />
);}}
/>
</View>
);
the relay_route.js should look something like
class relay_route extends Route {
static paramDefinitions = {
userID: { required: true }
}
static queries = {
user: () => Relay.QL`
query {
user(id: $userID)
}
`
}
static routeName = 'UserRoute'
}
And My TestComponent looks like:-
class TestComponent extends Component {
render () {
const user = this.props.user
return (
<Text>name: {user.name}</Text>
)
}
}
export default TestComponent = Relay.createContainer(TestComponent, {
fragments: {
user: () => Relay.QL`
fragment on User {
id,
name
}
`
}
})
For any doubts regarding relay, this documentation is classy to help us
Related
I am creating a blog using Next.js and mdx.
I began by following this entire tutorial. He made the blog using nextjs and md.
Then, I followed one part in this tutorial to switch from using md to mdx
However, the one line of code below is causing the error in the picture for some reason
<MDXRemote {...props.mdxSource} components={components} />
entire code
import fs from 'fs'
import matter from 'gray-matter'
import { serialize } from 'next-mdx-remote/serialize'
import { MDXRemote } from 'next-mdx-remote'
import path from 'path'
export default function Post(props) {
const components = {}
return (
<>
// this line of code is causing error
<MDXRemote {...props.mdxSource} components={components} />
</>
)
}
export async function getStaticProps(context) {
const markdownWithMeta = fs.readFileSync(
path.join('posts', slug + '.mdx'),
'utf-8'
)
const { content } = matter(markdownWithMeta)
const mdxSource = await serialize(content)
return {
props: {
mdxSource,
},
}
}
Package I'm using: https://github.com/hashicorp/next-mdx-remote
Instead of downgrading, you can use { development: false } as suggested in github issues.
const mdxSource = await serialize(source, {
mdxOptions: { development: false },
});
https://github.com/hashicorp/next-mdx-remote/issues/324
Same issue. Downgrade to "^3.0.8"
It works if you force-downgrade #mdx-js to 2.1.5:
Add to you package.json:
yarn
"resolutions": {
"#mdx-js/mdx": "2.1.5",
"#mdx-js/react": "2.1.5"
},
npm
"overrides": {
"#mdx-js/mdx": "2.1.5",
"#mdx-js/react": "2.1.5"
}
You need use npm run build & npm run start because you need to generate static pages
I am trying to get up and running with react create app and mobx state tree. I keep getting
Support for the experimental syntax 'decorators-legacy' isn't currently enabled (4:1):
I never used react create app so I am not sure how to enable, I tried making a .babelrc file but that did not help
{
"presets": ["#babel/preset-env"],
"plugins": [
["#babel/plugin-proposal-decorators", { "legacy": true }]
]
}
import React, { Component } from "react";
import { observer, inject } from "mobx-react";
#inject("domainStores")
#observer
export default class MainComponent extends Component {
async componentDidMount() {}
render() {
return <div className="main-container">
helllo
</div>;
}
}
I am also open to suggestions if things have changed, I have not used the newest version of Mobx State tree so many there is a better way of doing this now?
If you only using MST without regular MobX stuff and only need inject and observer then you can use regular function syntax, it looks not that great, but does not require any setup:
export const WrappedComponent = inject("domainStores")(observer(Component)
// Or use export default here if you prefer
If you are using other features of MobX then in MobX 6 there is a new thing that will probably allow you to drop decorators (like computed, action and etc) altogether, makeAutoObservable:
import { makeAutoObservable } from "mobx"
class Store {
// Don't need decorators now
string = 'Test String';
setString = (string) => {
this.string = string;
};
constructor() {
// Just call it here
makeAutoObservable (this);
}
}
With that you don't even need decorator syntax to be enabled.
More info here
https://mobx.js.org/migrating-from-4-or-5.html
and
https://mobx.js.org/react-integration.html
Instead of using the old decorators proposal, you can use observer as a function on your components instead of as a decorator.
We can also use React's own context instead of inject, as the documentation states:
Note: usually there is no need anymore to use Provider / inject in new code bases; most of its features are now covered by
React.createContext.
This way we can use Create React App as is.
Example
import React from "react";
import { observer } from "mobx-react";
import { types } from "mobx-state-tree";
const StoresContext = React.createContext("store");
// custom hook that we can use in function components to get
// the injected store(s)
function useStore() {
return React.useContext(StoresContext);
}
const StoreModel = types.model({
things: types.array(types.string)
});
const storeInstance = StoreModel.create({ things: ["foo", "bar"] });
// instead of using the #observer decorator, we can use observer as
// a function and give it a component as argument
const MainComponent = observer(() => {
const store = useStore();
return (
<div>
{store.things.map((thing) => (
<div key={thing}>{thing}</div>
))}
</div>
);
});
export default observer(function App() {
return (
<StoresContext.Provider value={storeInstance}>
<MainComponent />
</StoresContext.Provider>
);
});
CRA does not allow you to extend your own configuration, so, in order to extend cra configuration, you will have to use customize-cra with react-app-rewired.
So, follow the steps below:
Install customize-cra, react-app-rewired and #babel/plugin-proposal-decorators using npm or yarn
add config-overrides.js at root level of your project and paste the code given below:
const {override, addDecoratorsLegacy } = require('customize-cra');
module.exports = override(addDecoratorsLegacy());
Update package.json scripts to the below ones:
"start": "react-app-rewired start",
"build": "react-app-rewired build"
P.S: if you want to use babel configuration then your config-overrides.js should be like:
const {override, addDecoratorsLegacy, useBabelRc } = require('customize-cra');
module.exports = override(addDecoratorsLegacy(), useBabelRc());
useBabelRc will load config from your root of project automatically.
My Problem :
I expect my FirebaseProvider function to provide an object containing all functions, through the app. The problem is that all functions are well provided through my files, except my last new function : fetchTest.
Explainations :
If I click the TestPage.js button I get Uncaught TypeError: fetchTest is not a function.
I saw many posts on stackoverflow about this type of error, but none did help me. -> I think the original problem is the index.js is not called. The console.log("firebaseprovider") (in index.js) does not appear in console, yet the other files of the project in web-app/src/views/ have the same imports and exports than TestPage.
Since App.js code worked fine on all the other files, I don't know how console.log("firebaseprovider") is never displayed in the navigator console. (edit: no matter which page I go, this console.log never appears)
<FirebaseProvider> seems to not provide TestPage.js.
Do you have an idea ?
What I've tried :
placing a console.log in TestPage.js : it shows every function written in index.js but not fetchTest. It seems to not be properly exported through api object.
in TestPage.js trying console.log("api.fetchTest") : console displays undefined.
add a second testing function in index.js, whithout parameters, which just does console.log("test")
compare imports/exports and api declarations with other files in web-app/src/views/
create a handleSubmit() function in TestPage.js to not put the functions directly in return
delete node_modules and then yarn install
yarn workspace web-app build and then relaunch yarn workspace web-app start
(This is a Yarn Workspaces project containing a common/ and a web-app/ folders)
common/src/index.js:
import React, { createContext } from 'react';
import {FirebaseConfig} from 'config';
const FirebaseContext = createContext(null);
const FirebaseProvider = ({ children }) => {
console.log("firebaseprovider"); // is not displayed in the console
let firebase = { app: null, database: null, auth: null, storage:null }
if (!app.apps.length) { // I tried to comment out this line (and the '}') -> no difference
app.initializeApp(FirebaseConfig); // no difference when commented out
firebase = {
app: app,
database: app.database(),
auth: app.auth(),
storage: app.storage(),
// [ ... ] other lines of similar code
api : { // here are functions to import
fetchUser: () => (dispatch) => fetchUser()(dispatch)(firebase),
addProfile: (details) => (dispatch) => addProfile(userDetails)(dispatch)(firebase),
// [ ... ] other functions, properly exported and working in other files
// My function :
fetchTest: (testData) => (dispatch) => fetchTest(testData)(dispatch)(firebase),
}
}
}
return (
<FirebaseContext.Provider value={firebase}>
{children}
</FirebaseContext.Provider>
)
}
export { FirebaseContext, FirebaseProvider, store }
web-app/src/views/TestPage.js:
import React, { useContext } from "react";
import { useDispatch } from "react-redux";
import { FirebaseContext } from "common";
const TestPage.js = () => {
const { api } = useContext(FirebaseContext);
console.log(api); // Displays all functions in api object, but not fetchTest
const { fetchTest } = api;
const dispatch = useDispatch();
const testData = { validation: "pending" };
return <button onClick={ () => {
dispatch(fetchTest(testData)); // Tried with/without dispatch
alert("done");
}}>Test button</button>
}
export default TestPage;
web-app/src/App.js:
import React from 'react';
import { Router, Route, Switch } from 'react-router-dom';
// ... import all pages
import { Provider } from 'react-redux';
import TestPage from './views/CreateSiteNeed'; // written same way for the other pages
import { store, FirebaseProvider } from 'common';
function App() {
return (
<Provider store={store}>
<FirebaseProvider>
<AuthLoading>
<Router history={hist}>
<Switch>
<ProtectedRoute exact component={MyProfile} path="/profile" />
<!-- [ ... ] more <ProtectedRoute /> lines, form imported Pages line 3. -->
<ProtectedRoute exact component={TestPage} path="/testpage" />
</Switch>
</Router>
</AuthLoading>
</FirebaseProvider>
</Provider>
);
}
export default App;
I hope some people will find this post helpful, thanks
Here was the problem :
Firstly :
I'm using Redux, so fetchTest() has its testActions.js and testReducer.js files, which are functionnal. But I did forget to update my store.js :
// [ ... ] import all reducers
import { testReducer as testData } from '../reducers/testReducer'; // was'nt imported
const reducers = combineReducers({
auth,
usersdata,
// [ ... ] other imported reducers
testData // My test reducer
}
// The rest is a classic store.js code
Secondly :
As I'm using Yarn Workspaces, I had to compile the code in common/dist/index.js to make it accessible through the whole entire code (even for local testing).
Here is the command to compile the code (-> to include all redux edits made above) and make it accessible to web-app workspace :
yarn workspace common build && yarn workspace web-app add common#1.0.0 --force
Explanations on the second part of the command (yarn workspace web-app add common#1.0.0 --force) :
The web-app/package.json file contains { "dependencies": { ... "common":"1.0.0" ... }}
I am trying to follow this tutorial.
I'm currently stuck at the step which introduces react context to firebase.
This code block is the source of the current problem:
import Firebase, { FirebaseContext } from './components/firebase';
ReactDOM.render(
<FirebaseContext.Provider value={new Firebase()}>
<App />
</FirebaseContext.Provider>,
document.getElementById('root'),
);
serviceWorker.unregister();
When I try this, I get an error that says:
TypeError:_components_firebase__WEBPACK_IMPORTED_MODULE_5__.default is not a constructor
I have seen this post, which relates to Vue but says that the cause of an error with .default is not a constructor (not the rest of it), is because Firebase object should not be called with new keyword.
I tried removing new, but that generates an error message that says:
TypeError: Object(...) is not a function
I'm wondering if this has anything to do with the unusual way that the tutorial configures the app for firebase - which is with a class that uses a constructor (still don't understand why this is done this way):
class Firebase {
constructor() {
app.initializeApp(config);
this.auth = app.auth();
this.db = app.database();
}
export default Firebase;
Does anyone using Firebase with React know how to use the context API and if you do, have you found a way around this problem?
The firebase config setup files are:
index:
import FirebaseContext, { withFirebase } from './Context';
import Firebase from '../../firebase.1';
export default Firebase;
export { FirebaseContext, withFirebase };
context:
import React from 'react';
const FirebaseContext = React.createContext(null);
export const withFirebase = Component => props => (
<FirebaseContext.Consumer>
{firebase => <Component {...props} firebase={firebase} />}
</FirebaseContext.Consumer>
);
export default FirebaseContext;
NEXT ATTEMPT
I found this post, which looks like it might have been trying to follow the same tutorial.
That approach requires that I add back the auth method in the firebase.1.js config file so that it now looks like this:
class Firebase {
constructor() {
app.initializeApp(config).firestore();
this.auth = app.auth();
// this.db = app.database();
// this.db = app.firebase.database()
this.db = app.firestore();
}
doCreateUserWithEmailAndPassword = (email, password) =>
this.auth.createUserWithEmailAndPassword(email, password);
}
Then, the submit handler in the form is like this:
handleCreate = values => {
values.preventDefault();
const { name, email, password } = this.state;
Firebase
.doCreateUserWithEmailAndPassword = (email, password) => {
return this.auth
.createUserWithEmailAndPassword(email, password)
.then((res) => {
Firebase.firestore().collection("users").doc(res.user.uid).set({
email: values.email,
name: values.name,
role: values.role,
createdAt: Firebase.FieldValue.serverTimestamp()
}).then(() => this.history.push(ROUTES.DASHBOARD));
})
.catch(err => {
console.log(err.message);
});
};
};
When I try this, I don't get any errors, but the form does not submit - it just hangs.
NEXT ATTEMPT
Since the FirebaseContext.Consumer includes a line with firebase in lowercase, I tried all of the same steps above, but replacing title case Firebase with lower case firebase. I also tried this.firebase (I don't know why) and this.props.firebase (I have seen other posts try that but still don't know why).
None of these approaches work either.
When I try to console.log(Firebase) above the FirebaseContext.Provider, I get this a big log with lots of drop down menus that starts with this:
FirebaseAppImpl {firebase_: {…}, isDeleted_: false, services_: {…},
tokenListeners_: Array(0), analyticsEventRequests_: Array(0), …}
INTERNAL: {analytics: {…}, getUid: ƒ, getToken: ƒ,
addAuthTokenListener: ƒ, removeAuthTokenListener: ƒ}
One of the drop down menus inside this log is labelled "options_" and includes my firebase app credentials.
I think this could be related to the problem in my recent other answer.
I managed to get new Firebase working by simplifying the import/export setup.
In the react app the imports are now:
import { FirebaseContext } from './components/context';
import Firebase from './components/firebase';
Those imports and the new work after I replaced the complicated "import and re-export" code in the components/firebase.js with simply the definition for the class Firebase from the tutorial.
After this another error Firebase: Firebase App named '[DEFAULT]' already exists (app/duplicate-app). pops up, but that could be because I didn't follow the whole tutorial and some configuration is missing.
Replicating everything you are dealing with is quite difficult without having the full source of your project.
Here is the test App.js I used after the npx create-react-app react-firebase-authentication and after installing firebase.
import React from 'react';
import logo from './logo.svg';
import './App.css';
import { FirebaseContext } from './components/context';
import Firebase from './components/firebase';
function App() {
const foo = new Firebase()
return (
<div className="App">
... omitted ...
</div>
);
}
export default App;
I use this code to margin my Button from top:
const makeTopMargin = (elem) => {
return styled(elem)`
&& {
margin-top: 1em !important;
}
`;
}
const MarginButton = makeTopMargin(Button);
and whenever i use MarginButton node, I get this error: Warning: PropclassNamedid not match. Server: "ui icon left labeled button sc-bwzfXH MjXOI" Client: "ui icon left labeled button sc-bdVaJa fKCkqX"
You can see this produced here.
What should I do?
This warning was fixed for me by adding an .babelrc file in the project main folder, with the following content:
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
See following link for an example:
https://github.com/nblthree/nextjs-with-material-ui-and-styled-components/blob/master/.babelrc
Or you could just add this to your next.config.js. This also makes it so next-swc (speedy web compiler) works to reduce build times. See more here.
// next.config.js
module.exports = {
compiler: {
// Enables the styled-components SWC transform
styledComponents: true
}
}
You should install the babel plugin for styled-components and enable the plugin in your .babelrc
npm install --save-dev babel-plugin-styled-components
.babelrc
{
"plugins": [
[
"babel-plugin-styled-components"
]
]
}
The main reason I am posting this answer to help people understand the tradeoff. When we're using .babelrc in next project it's going to opt of SWC compiler which is based on Rust (Learn More).
It's going to show message something like this when you opt for custom bable config.
info - Disabled SWC as replacement for Babel because of custom Babel configuration ".babelrc"
I did more digging on this to only find out following! Ref
Next.js now uses Rust-based compiler SWC to compile
JavaScript/TypeScript. This new compiler is up to 17x faster than
Babel when compiling individual files and up to 5x faster Fast
Refresh.
So tradeoff was really huge, we can lose significant amout of performance. So I found a better solution which can solve this issue and keep SWC as default compiler.
You can add this experimental flag in your next.config.js to prevent this issue. Ref
// next.config.js
module.exports = {
compiler: {
// ssr and displayName are configured by default
styledComponents: true,
},
}
If you have already added babel plugins, delete the .next build folder & restart the server again
credit: Parth909 https://github.com/vercel/next.js/issues/7322#issuecomment-912415294
I was having the exact same issue and it was resolved by doing:
npm i babel-preset-next
npm install --save -D babel-plugin-styled-components
and adding this to .babelrc file:
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
Styled components server side rendering
Server Side Rendering styled-components supports concurrent server
side rendering, with stylesheet rehydration. The basic idea is that
everytime you render your app on the server, you can create a
ServerStyleSheet and add a provider to your React tree, that accepts
styles via a context API.
This doesn't interfere with global styles, such as keyframes or
createGlobalStyle and allows you to use styled-components with React
DOM's various SSR APIs.
import { renderToString } from 'react-dom/server'
import { ServerStyleSheet } from 'styled-components'
const sheet = new ServerStyleSheet()
try {
const html = renderToString(sheet.collectStyles(<YourApp />))
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement();
} catch (error) {
// handle error
console.error(error)
} finally {
sheet.seal()
}
import { renderToString } from 'react-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
const sheet = new ServerStyleSheet()
try {
const html = renderToString(
<StyleSheetManager sheet={sheet.instance}>
<YourApp />
</StyleSheetManager>
)
const styleTags = sheet.getStyleTags() // or sheet.getStyleElement();
} catch (error) {
// handle error
console.error(error)
} finally {
sheet.seal()
}
In my case as im using nextjs
import Document, { Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const sheet = new ServerStyleSheet();
const page = renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
);
const styleTags = sheet.getStyleElement();
return { ...page, styleTags };
}
render() {
return (
<html>
<Head>{this.props.styleTags}</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
I have solved this issue following these steps.
Create a file named .babelrc in the root directory and configure the .babelrc file.
delete the .next build folder(It stores some caches).
Restart the server.
Hot reload the browser.
.babelrc configuration file
{
"presets": [
"next/babel"
],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": false
}
]
]
}
PropType errors are runtime errors that will let you know that the data expected being passed to a prop is not what is expected. It looks like the className prop that is being set on your component is not the same when the component is rendered on the server and when it is then rendered in the client's DOM.
Since it looks like you are using server side rendering, you need to make sure that your class names are deterministic. That error is showing you the class that is being created by your styled-components library on the server and how it is different from the DOM. For libraries that do not normally have deterministic class names, you need to look at advanced configurations. Take a look at the styled-components documentation regarding specificity as it pertains to SSR.
//1. I got an error when using material-ui with Next.js
/********************************************* */
//2. The code I imported was like this :
const useStyles = makeStyles({
root: { // root must change
width: 100 ,
}
});
const Footer = () => {
const classes = useStyles();
return (
<div className={classes.root} > { /* root must change */}
<p> footer copyright #2021 </p>
</div>
)
}
export default Footer;
/********************************************* */
//3. I changed the code like this :
const useStyles = makeStyles({
footer: { // changed here to footer
width: "100%",
backgroundColor: "blue !important"
}
});
const Footer = () => {
const classes = useStyles();
return (
<div className={classes.footer} > { /* changed here to footer */}
<p> footer copyright #2021 </p>
</div>
)
}
export default Footer;
// I hope it works
For Old versions form Nextjs < 12, Go to next.config.js file and add this line inside nextConfig object:
experimental: {
// Enables the styled-components SWC transform
styledComponents: true
}
for new NextJs above 12:
compiler: {
styledComponents: true
}
if that does not work you need to make an NO SSR component wrapper like this:
// /components/NoSsr.js
import dynamic from 'next/dynamic'
const NoSsr = ({ children }) => <>{children}</>
export default dynamic(() => Promise.resolve(NoSsr), { ssr: false })
Then you need to add warp No SSR with your component like this:
// /pages/index.js
import NoSsr from '../components/NoSsr'
import CircleButton from '../components/buttons/CircleButton'
const HomePage = () => {
return (
<>
<p>Home Page Title</p>
<NoSsr>
{/* Here your styled-component */}
<makeTopMargin ele={...} />
</NoSsr>
</>
)
}
I'm using NextJS 12 and encountered the same issue, well error in the console, code was working ok.
I fixed it by creating a .babelrc file at the root of the project and add:
{
"presets": [
"next/babel"
],
"plugins": [
[
"styled-components",
{
"ssr": true,
"displayName": true,
"preprocess": false
}
]
]
}
Styled Components have full, core, non-experimental support in Next now (2022), but you have to turn them on:
Add the following to your next.config.js:
compiler: {
styledComponents: true,
},
My full, mostly vanilla, next.config.js now looks like this:
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
swcMinify: true,
compiler: {
// Enables the styled-components SWC transform
styledComponents: true,
},
}
module.exports = nextConfig
https://nextjs.org/blog/next-12-1#improved-swc-support
I followed all the other advice, around setting up .babelrc (or .babelrc.js), but noticed this message in the Next.js docs:
When css-in-js libraries are not set up for pre-rendering (SSR/SSG) it will often lead to a hydration mismatch. In general this means the application has to follow the Next.js example for the library. For example if pages/_document is missing and the Babel plugin is not added.
That linked to this file, showing that I needed to add this to pages/_document.tsx to:
// if you're using TypeScript use this snippet:
import React from "react";
import Document, {DocumentContext, DocumentInitialProps} from "next/document";
import {ServerStyleSheet} from "styled-components";
export default class MyDocument extends Document {
static async getInitialProps(
ctx: DocumentContext,
): Promise<DocumentInitialProps> {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
};
} finally {
sheet.seal();
}
}
}
A blog post by Raúl Sánchez also mentions this solution, linking to the JavaScript version if you're not using TS (pages/_document.js):
// if you're *not* using TypeScript use this snippet:
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
}
} finally {
sheet.seal()
}
}
}
If you are using create-react-app, you can use thi solution.
File called styled.ts
import styled from 'styled-components/macro';
import { css } from 'styled-components';
export const ListRow = styled.div`
...
...
`
Based on the files name, the prefix will be as following.
`${file_name}__{styled_component_name} ${unique_id}`
Meaning when implemented it will have the following classname
Although it would be nice to specify from where the first prefix would be taken from, meaning instead of file_name, we take folder_name. I currently dont know the solution for it.
To expand on C. Molindijk's answer, this error occurs when server-side class is different from client-side because styled-components generates its own unique class Id's. If your Next app is server-side rendered, then his answer is probably correct. However, Next.Js is by default statically generated, so unless you enabled SSR, configure it like this without ssr set to true:
{
"presets": ["next/babel"],
"plugins": [["styled-components"]]
}
This answer is for those who are using NextJs version > v12.0.1 and SWC compiler. You don't have to add _document.js file nor do babel related stuff anymore since it has been replaced by SWC compiler since v12.0.0. Only that your next.config.js file should look like the following since NextJs supports styled components after v12.1.0 and restart the server and it should work: more here
/** #type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
// add the following snippet
compiler: {
styledComponents: true,
},
};
module.exports = nextConfig;