The amount of work that goes into styling is a crucial aspect of development. The first language that comes to mind while aiming to alter the presentation of our web content is CSS.
CSS stands for Cascading Style Sheets and is a powerful rule-based language for establishing both the aesthetic and structure side of content.
Next.js, being a web development framework, is not a stranger to CSS. Therefore, we can easily incorporate styling in this framework.
Fortunately for us, Next.js provides its users with 5 ways to use CSS within a Next project.
Global CSS: A CSS file that allows us to apply specified CSS globally.
Tailwind CSS: A CSS framework that provides rapid designs by utility classes.
CSS Modules: Local scope CSS classes that solve name concerns and conflicts.
CSS in JS: A new way of embedding CSS within Javascript's components.
Sass: A CSS extension built on top of CSS with a wide range of capabilities.
Among these, Global CSS, CSS Modules, and CSS in JS are three ways that do not require external entities such as Tailwind, etc. We'll be covering these three concepts primarily.
Remember the CSS that we generally add in simple HTML / CSS / JS applications? That is exactly what we're talking about here!
Global CSS refers to CSS styles that can be applied to any component globally anywhere within the application. With this, our main aim is to define styles in a centralized way. A centralized way means the separation of the CSS stylesheet and the rest of the application.
Note: A consistent visual appearance can be achieved through this method.
body{font-size: 20px;margin: 10px;padding: 15px;height: 100vh;}.btn{background-color: white;}
Lines 1–6: We define a font-size
of 20 pixels, margin
and padding
of 10 and 15 pixels respectively, and the total height of our main body to be 100% of the view height vh
.
Lines 8–10: We add styles to our custom btn
class. A white
background-color
is assigned to it.
import '../styles.css';export function MyButton() {return(<buttontype = 'button'className = 'btn'>Join Educative here!</button>)}
import '../styles.css';export function MyButton2() {return(<buttontype = 'button'className = 'btn'>Check our Educative Courses!</button>)}
Line 1: The first and foremost task we perform is to import the global CSS file in both of our button files. Now, each class mentioned there is accessible in these files.
Lines 3–13: We can now add the btn
class in any file by simply assigning the className
a value of btn
. The styles applied to the body
will be visible in both components as well.
Ease of use due to familiarity
Consistent styling
Application wide usage
Now let's suppose we want to alter our global CSS concept a bit and add modularity to it. We can choose to do this when we aim to practice separation of concerns and solving naming conflicts is pivotal.
Note: Each CSS class we define in a module is scoped only locally to the page it is imported in.
CSS module usage is an approach that gives us the opportunity to write local scope CSS classes, where each content component is bound to a different style component.
.submitBtn {font-size: 15px;border: 1px solid black;background-color: green;}
Lines 1–5: We define a font-size
of 15 pixels, a border
with a width of 1 pixel, solid
lines, and black
color. The background-color
of our button is kept green
.
import styles from './MyButton.module.css';export function MyButton() {return(<buttontype = 'button'className = {styles.submitBtn}>Join Educative here!</button>)}
Line 1: Since we're dealing with modular styling now, we will import the styles specific to the MyButton
module. These are defined in MyButton.module.css
. We further use an alias name styles
for the import so that no naming conflicts occur.
Lines 3–13: The submitBtn
in the CSS file is distinguished by using the keyword styles.submitBtn
.
Note: If a class of the same name as 'submitBtn' belongs to another module, say
styles2
, usingstyles2.submitBtn
will not lead to any naming concern.
Preventing style or name issues
Scoped styling
Better reusability and maintainability
Pop Quiz on CSS modules!
Why don’t CSS modules have naming conflicts?
CSS in JS is an approach through which we can involve CSS styling within a JavaScript component. This helps us accomplish dynamic or scoped styling. Instead of separating our styles and component code, we can merge the two and get the best of both worlds.
By dynamic styling, we mean the ability to dynamically generate styles based on the props or state of our application.
Note: These styles can also be changed based on interactions or events.
function MyComponent() {return (<div><h2>Hello, Next.js!</h2><p>This is some scoped text</p><style jsx>{`h2 {color: green;}div {background: yellow;}@media (max-width: 750px) {div {background: blue;}}`}</style><style jsx global>{`body {background: blue;}`}</style></div>);}export default MyComponent;
Lines 6–7: A few fundamental HTML tags such as a level two heading h2
that says "Hello, Next.js" and a paragraph p
that says "This is some scoped text" are rendered on our page.
Lines 9–29: To add a few aesthetics to our content, we can now make use of styling. This way introduces CSS within JavaScript components. For this purpose, we can opt for two further methods.
We enclose our local styling i.e. the styling only applied to the elements of this component within a <style jsx>
tag and then write CSS as it is within the tags. In the example, the styles defined for h2
and div
are scoped to the enclosing MyComponent
component.
Another feature provided by CSS in JS is the <style jsx global>
tag. This is brilliant for also adding styles to the whole HTML and applying them to the whole application. In the example, the body
style is defined using jsx global
, which means it will apply to the entire body of the rendered HTML, regardless of the component hierarchy.
Line 31: We export the component so that it can be rendered in other components.
Users also have the option to disable JavaScript in production. In this case, the styling through CSS still gets loaded.
Dynamic styling
Scoped styling
JavaScript integration
We have now understood the dynamics of CSS in Next.js, and we highly encourage you to try it out! Lastly, to summarize, we have put forth a condensed view of some notable points for each method.
CSS Modules | Global CSS | CSS in JS | |
Definition | Locally scoped CSS classes. | Applied globally throughout the app. | Embed CSS directly in JavaScript components. |
Pros | Prevents naming conflicts. Improved maintainability. Easy to manage and organize styles. | Familiar for traditional CSS developers. Easy to use and understand. Simple setup and usage. | Dynamic and scoped styling. Ability to use JavaScript features like variables and mixins. Component-based styling with encapsulation. |
Cons | Requires importing and using class names. | Can lead to larger CSS bundles. | Potential performance overhead . |
Learning curve | Extra learning curve for newer users. | Less learning curve but difficult to manage styles as app grows. | Learning curve for new people to CSS in JS solutions. |
Usage | Component-level styling with encapsulation. | Global styles and application-wide styles. | Dynamic styling and complex styling requirements. |
Syntaz | Import styles from './styles.module.css'; | import '../global.css'; | <style jsx> or <style jsx global> |
Pop Quiz on Next.js Styling!
Where is Tailwind an optimal choice?
In naming conflicts
In global scope
In dynamic scope
Quick designs through class names
Free Resources