Next.js - import css file does not work - javascript

I am creating a project with react, redux and next.js, and want to import CSS files in js.
I followed instructions in next.js/#css and next-css, but find out that CSS styles do not work.
My code is as follow:
pages/index.js:
import React from 'react'
import "../style.css"
class Index extends React.Component {
render() {
return (
<div className="example">Hello World!</div>
);
}
}
export default Index
next.config.js:
const withCSS = require('#zeit/next-css')
module.exports = withCSS()
style.css:
.example {
font-size: 50px;
color: blue;
}
package.json:
{
"name": "my-app",
"version": "0.1.0",
"private": true,
"dependencies": {
"#zeit/next-css": "^0.1.5",
"next": "^6.0.0",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-redux": "^5.0.7",
"react-scripts": "1.1.4",
"redux": "^4.0.0",
"redux-devtools": "^3.4.1"
},
"scripts": {
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"dev": "next",
"build": "next build",
"start": "next start"
}
}
Questions:
1. There is an "Uncaught SyntaxError" in Chrome, but it seems to not affect the rendering of the page. But I still wondering the reason and the solution. index.js error in chrome is below img
2. As shown in Chrome, there's no "example" class, which means the style.css file is not loaded. Am I missing anything? no CSS file in chrome
Thanks in advance.

EDIT 2: As of Next.js > 10, you can import a global CSS file into _app.js, and you can use CSS modules in your components. More in the Next.js docs.
EDIT: As of Next.js 7, all you have to do to support importing .css files is to register the withCSS plugin in your next.config.js. Start by installing the plugin as dev dependency:
npm install --save-dev #zeit/next-css
Then create the next.config.js file in your project root and add the following to it:
// next.config.js
const withCSS = require('#zeit/next-css')
module.exports = withCSS({/* my next config */})
You can test that this is working by creating a simple page and importing some CSS. Start by creating a CSS file:
// ./index.css
div {
color: tomato;
}
Then create the pages folder with an index.js file. Then you can do stuff like this in your components:
// ./pages/index.js
import "../index.css"
export default () => <div>Welcome to next.js 7!</div>
You can also use CSS modules with a few lines of config. For more on this check out the documentation on nextjs.org/docs/#css.
Deprecated: Next.js < 7:
You'll also need to create a _document.js file in your pages folder and link to the compiled CSS file. Try it out with the following content:
// ./pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
render() {
return (
<html>
<Head>
<link rel="stylesheet" href="/_next/static/style.css" />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
The stylesheet is compiled to .next/static/style.css which means that the CSS file is served from /_next/static/style.css, which is the value of the href attribute in the link tag in the code above.
As for the first question, it's probably Chrome not understanding the import syntax. Try to enable the Experimental Web Platform flag in chrome:flags and see if that solves it.

For anyone who comes here ,the new Next JS supports CSS out of the box. The catch is that for modules (components), they must be named as the component. So, if you have a header inside a components directory, it must be named header.module.css
built-in-css-module-support-for-component-level-styles

Add {name}.css to src/static/styles/.
Then modify the Head in src/pages/_document.js to include the following link:
<Head>
<link href="/static/styles/{name}.css" rel="stylesheet">
</Head>

for next above 9.3, global css is written in "styles/globals.css" and you can import it to _app.js
import "../styles/globals.css";
Then for each component, you can write its own css and import it into the component. Pay attention to the naming:nameOfFile.module.css
Let's say you have "product.js" component and "product.module.css". you want to load css from "product.css" into "product.js"
import classes from "./product.module.css" // assuming it's in the same directory
you put all class names into product.module.css. Assume you have .main-product in product.module.css. Inside product.js, let's say you have a div to style
<div className={classes.main-product} > </div>
with the css module feature, you can use the same className in other components and it wont conflict. Because when next.js compiles, it will hash the name of the className, using its module. So hashed values of same classnames from different modules will be same

you need create to custom _document.js file.
Custom document when adding css will look like:
import React from "react";
import Document, { Head, Main, NextScript } from "next/document";
export default class MyDocument extends Document {
render() {
const { buildManifest } = this.props;
const { css } = buildManifest;
return (
<html lang="fa" dir="rtl">
<Head>
{css.map(file => (
<link rel="stylesheet" href={`/_next/${file}`} key={file} />
))}
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}

As Zeit said :
Create a /static folder at the same level the /pages folder.
In that folder put your .css files
In your page components import Head and add a to your CSS.
import Head from 'next/head'
function IndexPage() {
return (
<div>
<Head>
<title>My page title</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
<p>Hello world!</p>
</div>
)
}
export default IndexPage
And that's it, this way Next.js should render the link tag in the head of the page and the browser will download the CSS and apply it.
Thanks Sergiodxa at Github for this clear solution.

If you use next.js do this.
create next.config.js in root projects
const withCSS = require('#zeit/next-css');
function HACK_removeMinimizeOptionFromCssLoaders(config) {
console.warn(
'HACK: Removing `minimize` option from `css-loader` entries in Webpack config',
);
config.module.rules.forEach(rule => {
if (Array.isArray(rule.use)) {
rule.use.forEach(u => {
if (u.loader === 'css-loader' && u.options) {
delete u.options.minimize;
}
});
}
});
}
module.exports = withCSS({
webpack(config) {
HACK_removeMinimizeOptionFromCssLoaders(config);
return config;
},
});
Don't forget to restart the server

Global CSS Must Be in Your Custom <App>
Why This Error Occurred
An attempt to import Global CSS from a file other than pages/_app.js was made.
Global CSS cannot be used in files other than your Custom due to its side-effects and ordering problems.
Possible Ways to Fix It
Relocate all Global CSS imports to your pages/_app.js file.
Or, update your component to use local CSS (Component-Level CSS) via CSS Modules. This is the preferred approach.
Example:
// pages/_app.js
import '../styles.css'
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />
}

Set this to false if your app works directly with the web 5 package.
module.exports = {
// Webpack 5 is enabled by default
// You can still use webpack 4 while upgrading to the latest version of
// Next.js by adding the "webpack5: false" flag
webpack5: false,
}
You can use webpack 4.
yarn add webpack#webpack-4

Related

Next Js fails to load third party script in _document.js [duplicate]

I try to understand how the next.js Script tag with the strategy beforeInteractive works. For testing i just used lodash. But i keep getting a ReferenceError: _ is not defined. I thought when a script is loaded with beforeInteractive it should be globally available inside my page Component since it get injected into the initial Html from the server and i could use it for example in the useEffect hook to alter a div.
Can someone explain to me why it's not working or what i'm doing wrong?
I don't installed it via npm because im trying to figure out how it works.
I have a simple _document.js and i added a Next.js script tag with the strategy beforeInteractive to this _document.js. The next.js docs says:
This strategy only works inside _document.js and is designed to load scripts that are needed by the entire site (i.e. the script will load when any page in the application has been loaded server-side).
import { Html, Head, Main, NextScript } from 'next/document'
import Script from 'next/script'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<Script
src="https://unpkg.com/lodash#4.17.20"
strategy="beforeInteractive"
></Script>
</body>
</Html>
)
}
Then i have a simple page Component inside the pages folder. I added the getServerSideProps function to use ServerSideRendering.
If you export a function called getServerSideProps (Server-Side Rendering) from a page, Next.js will pre-render this page on each request using the data returned by getServerSideProps.
import Head from 'next/head';
import {useEffect, useState} from 'react';
const TestComponent = () => {
const [change,setChange] = useState('not changed');
useEffect(()=> {
console.log(_);
setChange(_.join(['one','two'],' - '));
});
return (
<>
<Head>
<title>Test</title>
</Head>
<div>{change}</div>
</>
);
};
export async function getServerSideProps(context) {
return {
props: {},
}
}
export default TestComponent;
Update
Seems like it is indeed a bug which is fixed but not released yet
https://github.com/vercel/next.js/discussions/37098
Putting aside the fact that you should be importing Lodash as a node module, there does seem to be an issue when using next/script in _document (no matter what the external script actually is).
It turns out this is a Next.js bug that has been addressed in this PR in pre-release version v12.1.7-canary.8. To fix the issue in your project simply update Next.js to version >=12.2.0 (npm install next#latest).
As an alternative, you can use the <script> tag directly in the _document's <Head> with the defer property. This closely matches what the next/script would output.
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head>
<script
type="text/javascript"
src="https://unpkg.com/lodash#4.17.20/lodash.js"
defer
></script>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
First and foremost, I'm failing to see virtually any reason you'd want to do this, when you can (and should) simply use install it to node_modules. You're also going to possibly run the risk of the bundle having issues if the library type isn't a module and the next configuration requires a module.
Solution based on the question:
There's two ways.
Firstly, see the docs on this exact thing.
Please use the above method mentioned in the docs.
If that's not an option for whatever reason...
The second is a less than ideal, but working solution.
Create a folder for your static files. Ex: <root>/static/js/hello.js. Then in your _document file,
<script type="text/javascript" src="/static/hello.js"></script>

vue-multiselect styles working in dev but not in prod

I have added <style src="vue-multiselect/dist/vue-multiselect.min.css"> to my vue component, its styles working when running npm run dev but not when running npm run prod. How to fix that?
<template>
<multi-select :id="id" v-model="value" :options="options" :multiple="multiple" :max="max"></multi-select>
</template>
<script>
import multiSelect from 'vue-multiselect';
export default {
name: "my-multi-select",
components: { multiSelect },
}
</script>
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
The problem is, that you using a relative path. What you need is to add a ref to your src folder, to tell vue-cli, that you want to include it in your production build.
<style>
#import './assets/styles/vue-multiselect.min.css';
</style>
The # refers your src path. Where you store it or what you call your folders is up to you.
EDIT: Another approach could be to reinstall the package with this command:
npm install vue-multiselect#next --save
Or just import it like you do with the component:
import Multiselect from 'vue-multiselect'
import 'vue-multiselect/dist/vue-multiselect.min.css'
EDIT 2: vue.config.js, create this file in your root. The content should be:
module.exports = {
publicPath: './'
};

Element UI icons wont show up using Laravel Mix

I made a simple website using Vue and Element UI. I used Laravel Mix to compile my code.
During development, the icons are showing up but when I run "npm run prod" and upload it to Github Pages they wont show up.
This is my webpack.mix.js
let mix = require('laravel-mix');
mix.js('src/js/app.js', 'public/')
.sass('src/styles/app.scss', 'public/')
.babelConfig({})
.disableNotifications();
I am using on demand components and followed this doc so my root vue file looks like this:
import Vue from 'vue'
import store from './vuex'
import router from './vue-router'
import Element from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import lang from 'element-ui/lib/locale/lang/es'
import locale from 'element-ui/lib/locale'
locale.use(lang)
Vue.use(Loading.directive);
Vue.component(Select.name, Select)
Vue.component(Option.name, Option)
Vue.component(Input.name, Input)
Vue.component(Icon.name, Icon)
new Vue({
el: '#app',
router,
store,
});
Following the same doc, I added a .babelrc file on my root directory but I didn't managed to get it working with the preset es2015 so I used #babel/preset-env instead. I dont actually know how to properly use Babel so the whole error might be over here but idk.
{
"presets": [["#babel/preset-env", { "modules": false }]],
"plugins": [
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "theme-chalk"
}
]
]
}
I noticed something weird, when I run npm run prod, the output shows something like this:
The fonts folder and the needed fonts are being copied to my root directory, so when its on Github it makes a request to the root domain, the root folder, the right url should be over (I guess?) /h3lltronik.github.io/my-site/ but it is on /h3lltronik.github.io/.
Just in case is needed, Im using the icons like this:
<el-input v-model="search" prefix-icon="el-icon-search" class="filter_input element-input bordered" #input="onChangeSearch"
placeholder="Search for a country..."></el-input>
And this is my index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>H3lltronik</title>
<link rel="stylesheet" href="./public/app.css">
</head>
<body>
<div id="app" :class="modeClass">
<transition name="el-fade-in">
<router-view class="content-body"></router-view>
</transition>
</div>
<script src="./public/app.js"></script>
</body>
</html>

Does order of imports in Webpack entry file matter

I'm having an issue where I have two components that I'm importing into my entry file "index.js" - depending on which I have as the first import, the second will not work and I get an error "Target Container is not a DOM Element." What am I missing? Why will the Proforma component only render if it's first in the index.js, and why does the App component try to load anyways when I browse to the page that the Proforma component loads (when it successfully loads and the console still has the above error)?
The index.js file looks like:
import App from "./components/App.js";
import Proforma from "./components/property_detail/proforma.js";
In that scenario, the page displaying App.js works - but still attempts to load Proforma despite that element not appearing anywhere in the App.js code. The same happens when I change the order of the elements and load the page that includes "proforma.js", which is just:
import React, { Component } from "react";
import ReactDOM from "react-dom";
class Proforma extends React.Component {
render() {
return (
<p>Proforma Page</p>
);
}
}
ReactDOM.render(
<Proforma />,
document.getElementById('proforma')
);
The template page that the Proforma component should be rendered on:
{% load static %}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static './css/bulma/bulma.css' %}">
<title>Test Proforma </title>
</head>
<body>
<section class="section">
<div class="container">
<div id="proforma"><!-- React --></div>
</div>
</section>
{% load static %}
<script src="{% static 'frontend/main.js' %}"></script>
</body>
</html>
For good measure, the package.json file and webpack.config look like:
I'm building the file with this:
package.json
{
"main": "index.js",
"scripts": {
"dev": "webpack --mode development
./simple_proforma_react/frontend/src/index.js --output
./simple_proforma_react/frontend/static/frontend/main.js",
....
}
webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: "babel-loader"
}
}
]
}
};
Do you have a ReactDom.render in your proforma component file, as well as in your app.js/index.js file? That might cause a problem

React - insertion of third party script tag inside render rather than page head causes app failure

I'm writing a React component to use Mozilla's PDF.js project to render a document in HTML. I've gotten it working successfully when the example's Create-React-App project includes
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/1.8.527/pdf.min.js"></script>
in the index.html head block. If I remove it and insert it in the component render method, the application breaks. Is there a way to add a <script> tag to the body of the application
Background
According to the examples, there are two ways you can add a document to a page: render each page with the canvas tag, or use pdfjs-dist/web/pdf-viewer.js
Code
Following the Webpack examples (since this is a React project), I came up with the following code:
import React, {Component} from 'react';
import pdflib from 'pdfjs-dist';
import {PDFJS} from 'pdfjs-dist/web/pdf_viewer'
import 'pdfjs-dist/web/pdf_viewer.css';
export default class PDF extends Component {
constructor(props) {
super(props);
pdflib.PDFJS.workerSrc = require('file-loader!pdfjs-dist/build/pdf.worker');
this.file = props.file;
this.pdfViewer = null;
this._containerId = 'viewer_container';
this.pagesInit = this.pagesInit.bind(this);
}
componentDidMount() {
this._loadDocument();
}
pagesInit(e) {
this.pdfViewer.currentScaleValue = 'page-width';
}
_loadDocument() {
const container = document.getElementById(this._containerId);
this.pdfViewer = new pdflib.PDFJS.PDFViewer({
container: container
});
container.addEventListener('pagesinit', this.pagesInit);
const loadingTask = pdflib.getDocument(this.file);
loadingTask.promise.then((document) => {
this.pdfViewer.setDocument(document);
}).catch((err) => {
console.log(err);
});
}
render() {
return(
<div id={this._containerId}>
<div className="pdfViewer" id="viewer"></div>
</div>
)
}
}
However, for a reason unknown to me, I need to include the build of pdf.js in the head of the index page of the application so it is loaded before the bundle.
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/1.8.527/pdf.min.js"></script>
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Without it, the document will not render. This seems directly related to script ordering.
Part of the convenience of an external React component is its self-contained, and this script requirement means this component certainly is not self contained.
I've attempted to use react-async-script-loader to wrap the component in a Higher Order Component and retrieve the script, but the same error happens.
Is there another solution I should look at?

Categories