Imagine I have an app directory, inside which there are page.tsx and layout.tsx files. I have set a header and footer in layout.tsx which will be shown on every route and between them there is
<main>{children}</main>
But just for the home page ("/" route) I want to display a div "before" the header. I don't want this div to be displayed on other routes like /about. What should I do? If I put this div inside page.tsx which is located inside app directory, it will be shown between header and footer. And by defining different layouts for other folders inside app directory, I can only add something to the root layout. But what I actually want to do here is subtract something (that div before the header) from root layout for other routes.
Taking inspiration from #Zain_UI_Din's answer which won't work when using the new /app directory. I believe you can do something similar in layout.tsx, but I think you have to make it a client component because you're using the router.
'use client'; // not sure if needed
import { useRouter } from 'next/router';
export default function RootLayout({ children }: { children: React.ReactNode }) {
const router = useRouter();
return (
<html lang="en">
<body>
{router.pathname === '/' && (
<div>Home page</div>
)}
<header>header content</header>
<main>{children}</main>
<footer>footer content</footer>
</body>
</html>
);
}
Alternatively, you could define different layouts for the home page and other pages using route groups, which would allow you to do this.
Related
I have the following pages: /, /accounts, /signin
I want to use a dashboard layout for the first 2 and a normal layout for the signin page. But i have no idea how i can achieve this. If i add the dashboard layout to the layout.tsx file i see it on the signin page and i can't override it there. If i add the layout to the page.tsx file, it kinda defeats the whole purpose of those layouts?
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head />
<body className="bg-gray-50">{children}</body>
</html>
);
}
vs
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<head />
<body className="bg-gray-50">
<LayoutComponent />
<div className="p-4 mt-16 sm:ml-64">{children}</div>
</body>
</html>
);
}
You could leave the /singin page in the main directory and create an additional directory for dashboard pages, e.g. app/(dashboard), putting dashboard in parenthesis means that it won't affect the URL, see the docs.
The structure would look like:
- app
- layout.js <- your main layout
- singin
- page.js
- (dashboard)
- page.js
- layout.js <- your dashboard layout
- accounts
- page.js
Just like the title, I try to use the createapp method to build component objects, and then mount to DOM nodes. When there are multiple mounts, all the previous ones are cleared. How can I mount continuously while retaining the previous node, just like "insertAfter" in jQuery.
You can do multiple mounts as below.
index.html in body tag
<body>
<div id="header"></div>
<div id="section1"></div>
</body>
main.js file or other file where you mount app.
const headerApp = createApp({
/* ... */
})
headerApp.mount('#header')
const sectionApp = createApp({
/* ... */
})
sectionApp.mount('#section1')
I am using jsx to define styles for the NextJs components and i need to define a background image for some elements.
The only paths that seems to accept is the relative path, to the current page, or absolute path.
But how to pass a relative path to the component itself?
Here a simple component function for testing:
import React from 'react'
const TestComponent = (props) => {
return (
<div>
<h1 className="test">See my background</h1>
<style jsx>{`
.test {
background-image: url("someImageFile.jpg");
}
`}</style>
</div>
)
}
export default TestComponent
This will return the 404 error for url "localhost:3000/current page/someImageFile.jpg"
For a NextJS application, you should have a public folder at the root of the directory of your app where you store all the static files: such as images.
Here is a link to the official documentation where they explain it very clearly:
https://nextjs.org/docs/basic-features/static-file-serving
The trick is to use $ to be able to call require function:
<style jsx>{`
.test{
background-image: url(${require("./someImageFile.jpg")});
}
`}</style>
Reference documentation here.
I am using Next.js with Typescript. The margin of the body is default 8px and I want to get it to 0px. When I try to add an external style sheet to my index.tsx file it throws an error that you can only add external stylesheet to _app.tsx. However, even when I try to import in my _app.tsx, it doesn't change the global style of the body. I am using Emotion css for the styling part. Is there a different way to change the style of the body in the index file using global style? Here is my index.tsx code and I have tried adding the global styles using Emotion CSS as well but it doesn't work.
class Index extends React.Component {
render() {
return (
<div className='body'>
<Container>
<style jsx global>{`
.body:global() {
margin: 0px;
}
`}</style>
<NavBar />
</Container>
</div>
);
}
}
You need some global styles (<style jsx global>) to style the body element or provide css resets.
Example:
import Link from "next/link";
export default () => (
<div>
<style jsx global>{`
body {
background-color: red;
}
`}</style>
Hello, One!
<Link href="/two">
<a>Go to two</a>
</Link>
</div>
);
Code Sandbox
You can have global styles using emotion with Next.js
In your _app.tsx file, you must to
import { Global, css } from '#emotion/core'
return (
<>
<Global styles={css` /* styles */ `}/>
<Component {...pageProps} />
</>
You can see how to implement it, here
https://github.com/pabloobandodev/social-media/blob/master/pages/_app.tsx
According to the official docs:
Global CSS cannot be used in files other than your Custom <App> due to its side-effects and ordering problems.
Possible Ways to Fix It
Relocate all Global CSS imports to your pages/_app.js file.
How to do this in your case?
Well, the best way is to use a CSS base, lets take normalize.css for example.
Run yarn add normalize.css or npm i normalize.css, depending on whichever you are using.
Add import 'normalize.css'; in each of the page you want to use the base on. Official Docs.
Well this could seem redundant if you want to use the base in all of your pages. If so, you can, alternatively, create a file page/_app.tsx (any of the extension .js,.jsx,.ts,.tsx will work) and put this in it:
import 'normalize.css';
export { default } from 'next/app';
Note : If your app is running and you just added a custom App, you'll need to restart the development server. Only required if pages/_app.tsx didn't exist before.
No need to worry about other caveats mentioned in the docs as we are simply re-exporting App without any modification.
There are many CSS bases available choose any that seems best for you.
If you want to add custom global styles, then follow this:
Create a file styles/globals.css (.scss,.sass,etc. will also work if you have configured Next.js properly) and put your styles in that file.
Now add an import in pages/_app.tsx.
import '../styles/globals.css'; // change extension from `.css` to
// whatever you created above
export { default } from 'next/app';
If you have already created a module path alias for ../styles, then you might wanna change the styles import statement (probably to something like import '#styles/globals.css').
Also, if you are using less/sass/scss and want to use a base at the same time along with your custom global styles you simply need to use an import statement in your stylesheet (no need to import the base in _app.tsx if imported in the global stylesheet). An example:
// file: styles/globals.scss
#import '../node_modules/normalize.css/normalize.css';
// your styles...
body {
color: red;
}
// file: pages/_app.tsx
import '#styles/globals.scss';
export { default } from 'next/app';
Moreover, in your case it has not worked most probably because you were styling .body instead of body. It is likely that margin was present in the body, not your div.body.
This is how your _app.js, _app.tsx should look like; styles.css may have your CSS to reset the default browser properties, you can try adding other stylesheets here.
import '../styles/styles.css'
export default function App({ Component, pageProps }) {
return <Component {...pageProps} />
}
When I route my app to another component by using react-router-dom, the CSS doesn't change.
This is a minimalistic version of the code to demonstrate
App.js
import React from 'react';
import Home from './Home';
function App() {
return (
<div>
<Home></Home>
</div>
);
}
export default App;
Home.js
import React from 'react';
import './Home.css';
const Home = () => {
return (
<h1>Home</h1>
);
}
export default Home;
Home.css
body {
background-color: blue;
}
Dashboard.js
import React from 'react';
import './Dashboard.css';
import React from 'react';
import './Dashboard.css';
const Dashboard = () => {
return (
<div className='content'>
<h1>Dashboard</h1>
</div>
);
}
export default Dashboard;
Dashboard.css
.content {
display: flex;
align-content: center;
align-items: center;
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import Dashboard from './Dashboard';
import * as serviceWorker from './serviceWorker';
import { BrowserRouter as Router, Route } from 'react-router-dom';
ReactDOM.render(
<Router>
<div>
<Route exact path='/' component={App} />
<Route path='/dashboard' component={Dashboard} />
</div>
</Router>, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: ...
serviceWorker.unregister();
When I do /dashboard, it loads the Dashboard component, but it keeps the previous CSS that was loaded from the Home component that resides the App component. The background stays blue. I want that when I route to another component because I changed the URL, it loads whatever CSS that new component has attached to it and gets rid of whatever CSS was before. Is that possible?
Edit: I have made an example in CodeSandbox to illustrate. It's a little different from the code above due to the limitations of the playground, but the functionality is the same.
From what can be seen, importing as a module ends up importing it globally. If we comment the line import Home from "./Home"; the blue background disappears. Just importing the component, imports the whole CSS despite the CSS being imported in a modular way. I'm not sure if I am missing something.
Edit 2:
Here are the different solutions I tried:
CSS Modules, but the body style was still globally loaded.
Styled components don't let me modify the body or html selectors CSS. They require me to create a <div> element and
then have that element span the whole body which I would style
as if it was the body. Which is a workaround I don't want to use because for that I rather use CSS Modules for the whole body spanning .
Inline styling also doesn't let me modify the body or html selectors CSS. I would also need to use a workaround like a body spanning <div> as in Styled components.
The problem
When you import a css like you're doing here
import './Home.css';
you're importing it in a global scope, which means it will not disappear once imported.
The solutions
CSS Modules
What you want is either CSS Modules, which is used like this:
import styles from './Home.css';
<a className={styles.myStyleClass}>Hello</a>
Styled components
or a CSS-in-js framework such as styled components which is used like this:
import styled from 'styled-components';
const MyStyledElement = styled.a`
color: blue;
`;
<MyStyledElement>Hello</MyStyledElement>
Regular objects / inline styling
or just "regular" CSS-in-js like:
const myStyle = {
color: blue;
}
<a style={myStyle}>Hello</a>
There are plenty of options when it comes to styling, these alternatives are popular ones that I encourage you to explore and see which you enjoy.
After doing some more tests I have concluded that as of now it is not possible to change whatever CSS styles have been applied to a <body> or <html> selector in an React SPA when a CSS file is already loaded and one uses React Router to render other components. I still appreciate the answers and the time taken to help me find a solution. They are still valid answers if we are not talking about the <body> or <html> node in an HTML document. From them I learned about other ways to use CSS in React. I modified the original post with the solutions I tried.
What ended working was modifying the DOM styles with JavaScript whithin the component itself.
Home.js
import React from "react";
const Home = () => {
// Modify the DOM Styles with JavaScript
document.body.style.backgroundColor = "blue";
// Or uncomment below to modify the
// document root background color
// which in this case would be <html>
//document.bgColor = "blue";
// Or modify the root tag style of the document instead of the
// <body> (<html> in this case)
//document.documentElement.setAttribute('style', 'background-color: green');
return (
<div>
<h1>Home</h1>
<form action="/dashboard">
<input type="submit" value="Go to Dashboard" />
</form>
</div>
);
};
export default Home;
Here is a working example:
Where my app wasn't loading style sheets and the like. However, I was importing my assets directly into my index.html entry point.
By replacing the links with absolute paths as per this documentation, my problem was resolved.
For me, this meant changing
<head>
<link rel="stylesheet" href="./style.css" ></link>
</head>
to this:
<head>
<link rel="stylesheet" href="/style.css" ></link>
</head>
I'm not sure if the same thing would work for your import statements, but it is worth a shot.
More info: styles-not-working-with-react-router