Is there a way to cancel nextjs 13 layout? - next.js

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

Related

How to export script and execute it with next/script

Salutations!
I am working on a Tailwind-css-Next.js-Typescript project trying to implement a theme toggler. It works fine except for the on-load flicker.
In the past I implemented a solution similar to https://github.com/vercel/next.js/discussions/12533 but now that only next/script is accepted, when using Typescript you can't export a script without export which paradoxically returns an Uncaught SyntaxError: Unexpected token 'export' error.Also scripts like this won't execute anyway if you add them in the Head instead of inside the body. Which in turn is a problem because tailwind wants the script in the head.
This is my current script (note that I must use export{} and create a module otherwise the Typescript linter doesn't let me save the file):
var theme = localStorage.getItem("dark-theme") || "light";
if (theme === "dark") {
document.documentElement.classList.add("dark");
}
export {};
And I try to use it like so:
import "./globals.css";
import Script from "next/script";
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
{/*
<head /> will contain the components returned by the nearest parent
head.tsx. Find out more at https://beta.nextjs.org/docs/api-reference/file-conventions/head
*/}
<head />
<body>
<Script
type="module"
strategy="beforeInteractive"
src="themeScript.tsx"
/>
{children}
</body>
</html>
);
}
But this causes a Uncaught SyntaxError: Unexpected token 'export' (at themeScript.tsx:1:1)
I have also tried adding the following both in the Head and in the body. It doesn't return an error but doesn't produce a result.
<Script id="find-dark-mode">{localStorage.getItem("dark-theme") === true ? document.documentElement.classList.add('dark') : document.documentElement.classList.remove('dark')}</Script>
This question is similar to Next 13 & Tailwind Darkmode Flickering but I am asking about next 13 scripts in general.
I appreciate your time in advance.

is it possible to put an element "before" layout component?

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.

How to make Vue3 test utils work with teleport

I have a component that uses teleport to , the test html doesn't seem to be working as expected. I can't find any documentation on this particular use. Here's my test:
describe('MetaHead', () => {
it('dynamic metadata tags contain custom text', () => {
let title = 'My Page';
let description = 'Some description about my page';
// This component uses Vue3's teleport to tag <head>
// we must modify wrapper to contain such tag
document.body.innerHTML = `
<head>
<div id="app"></div>
</head>
`
const wrapper = mount(MetaHead, {
attachTo: document.getElementById('app'),
props: {
title,
description
},
global:{
mocks: {
$route:{fullPath: 'full/path'}
}
}
})
expect(wrapper.html()).toContain(title)
expect(wrapper.html()).toContain(description)
})
})
and the minimal component looks like this:
<template>
<teleport to="head">
<title>{{title}}</title>
<meta property="og:site_name" :content="title">
<meta name="description" :content="description">
</teleport>
</template>
Am I missing something?
the problem here is wrapper.html() only returns HTML in your component - since you are teleporting outside your component, that markup won't show up when you call wrapper.html().
You have a few options. One would be making an assertion against document.body.outerHTML. Another would be using a neat trick with findComponent, I wrote about it here and posted a video about it here.
Another thing you could try that I just thought of (but have not tested) would be:
mount({
template: `
<div id="app" />
<MetaHead />
`,
components: { MetaHead }
})
I don't know if that will work, but worth a try.

How to import css file from assets folder in nuxt.js

<template>
<div class="container">
<head>
<link rel="stylesheet" href="~assets/css/style-light.css" />
<link rel="stylesheet" href="~assets/css/login-light.css" />
</head>
</div>
</template>
Importing css like above results in this error
vue.runtime.esm.js:5717 GET http://localhost:3000/~assets/css/login-light.css net::ERR_ABORTED 404 (Not Found)
Is there really no other way loading css other than putting the whole css in the template?
The first thing you need to know is, you can't declare a html head inside any place, neither in yours tamplate, neither in yours components, neither in yours pages, neither in nowhere.
Keep in mind that you can't use a html tags for this, you will use a json schema.
take a look https://nuxtjs.org/guide/configuration for more detailed explanations.
Now about you doubt if you want to import the CSS as globally, the correct place is inside your nuxt.config.js, inside this file, you have a property called head, and inside the head we will configure all the imports.
So, inside nuxt.config.js find your head session, and then create new property called css, some thing like this:
head: {
css: [
'~/assets/style/app.styl',
'~/assets/style/main.css'
],
}
...
Another way, is import your css directly inside your component, for this you can do some thing like this:
<style scoped>
#import '~/assets/style/main.css';
</style>
OR
<style scoped src="#/assets/styles/mystyles.css">
</style>
In Nuxt, you will need a CSS loader instaled in your application too, so have sure you had intalled a "stylus" and "stylus-loader" in your app.
try to impot your css files in script like this :
<script>
import "#/assets/css/style-light.css";
import "#/assets/css/login-light.css";
///
</script>
EDIT: changed ~ to #
You could bring your files in using the head method like so :
head () {
return {
link: [
{ rel: 'stylesheet', href: '/style-light.css' },
{ rel: 'stylesheet', href: '/login-light.css' }
]
}
}
You should also move these css files into the static folder. See this discussion on the Vue forum https://forum.vuejs.org/t/nuxt-import-css-file-and-js-file/42498

Universal rendering creates a delay between DOMContentLoaded event to Load event

I'm very excited about styled components and would love to use it if it wasn't for this...
I've prepared two example projects using next.js universal rendering library.
The first example is using styled-components as a solution, and the second one is using their default solution for css which is styled-jsx.
Both examples include exactly the same code with a minimum level of complexity.
As you will soon see for yourself - in the styled-components example there is a disturbing delay between DOMContentLoaded event and Load event inwhich the user actually sees the un-styled html markup, while in the second example using styled-jsx this is not the case.
Both demos are hosted online using Zeit now:
1 - https://01-styled-components-sqprkdqeft.now.sh
2 - https://02-styled-jsx-nhrynpsdox.now.sh
Source available on github:
1 - https://github.com/Ajar-Ajar/next-demo--styled-components
2 - https://github.com/Ajar-Ajar/next-demo--styled-jsx
I would very much appreciate any insights regarding why does it happen in one and not the other,
and of course any way to amend this behavior as I would love to use styled-components for its many features and advantages.
Thank you
Ajar
:)
What is missing here is the style injection on the server. Basically, when you write styles in JavaScript you have to get the generated styles on the server and inject them as a style tag into the generated HTML.
The built-in solution for Next does this automatically for you, with styled-components you have to do a tiny bit of manual work and add a pages/_document.js file that looks like this:
import Document, { Head, Main, NextScript } from 'next/document'
import { styleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps ({ renderPage }) {
const page = renderPage()
const styles = (
<style dangerouslySetInnerHTML={{ __html: styleSheet.rules().map(rule => rule.cssText).join('\n') }} />
)
return { ...page, styles }
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Notice how we inject a style tag with the styles from styled-components. That's all there is to it, now that flash of unstyled content is gone! 🎉 (this is taken from the official example)
Note: With v2 of styled-components (coming soon, you can get it right now with `npm i --save styled-components#next) there'll be an official API for SSR so it'll look more like this:
import Document, { Head, Main, NextScript } from 'next/document'
import styleSheet from 'styled-components/lib/models/StyleSheet'
export default class MyDocument extends Document {
static async getInitialProps ({ renderPage }) {
const page = renderPage()
const styles = (
<style dangerouslySetInnerHTML={{ __html: styleSheet.getCSS() }} />
)
return { ...page, styles }
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Hope that helps!
Here is the recommended way for using styled-components with next to avoid the issue:
https://github.com/vercel/next.js/blob/master/examples/with-styled-components/pages/_document.js
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()
}
render () {
return (
<html>
<Head>
<title>My page</title>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}

Resources