Common modular "template" for page(s)
Answered
Painted Redstart posted this in #help-forum
Painted RedstartOP
I'm making wiki-like pages using
The only method I can think of for accomplishing this is to have a parent class that generates the sidebar & header, and then extend that for each page, but I don't think I can do this with the
https://nextjs.org/docs/app/api-reference/file-conventions/template
Note: I keep this data in the metadata export because it allows me to also set the metadata for the site to this data.
Here's what I currently have:
How would I go about creating a "default template" that allows me to pass data for it to populate itself, and then embed a document in that templet?
mdx and would like to add a common(ly formated?) sidebar & header that contains information about the article (such as subheader anchors, date published, author etc.). Currently the header is child of the body of the article.The only method I can think of for accomplishing this is to have a parent class that generates the sidebar & header, and then extend that for each page, but I don't think I can do this with the
mdx format.template seems promising in this regard, but it also seems that the rendering process makes template a parent to a page, which means I cant send data back up from a page to a template so the template can use it.https://nextjs.org/docs/app/api-reference/file-conventions/template
Note: I keep this data in the metadata export because it allows me to also set the metadata for the site to this data.
Here's what I currently have:
import ArticleHeader from 'components/ArticleHeader'
export const metadata = {
title: 'A Title',
description: 'Small Description',
authors: [{name: 'I.M. Writer'}],
date: "12-25-97",
}
<ArticleHeader metadata = {metadata}/>
Some cool information goes here...How would I go about creating a "default template" that allows me to pass data for it to populate itself, and then embed a document in that templet?
Answered by Painted Redstart
oh, and here's a diagram i made while studying your code, i know you said it's a bit difficult to explain so actually mapping out the data flow was very useful. Couldn'tve pieced it together without your example!
15 Replies
@Painted Redstart I'm making wiki-like pages using `mdx` and would like to add a common(ly formated?) sidebar & header that contains information about the article (such as subheader anchors, date published, author etc.). Currently the header is child of the body of the article.
The only method I can think of for accomplishing this is to have a parent class that generates the sidebar & header, and then extend that for each page, but I don't think I can do this with the `mdx` format.
`template` seems promising in this regard, but it also seems that the rendering process makes `template` a parent to a `page`, which means I cant send data back up from a `page` to a `template` so the `template` can use it.
https://nextjs.org/docs/app/api-reference/file-conventions/template
Note: I keep this data in the metadata export because it allows me to also set the metadata for the site to this data.
Here's what I currently have:
mdx
import ArticleHeader from 'components/ArticleHeader'
export const metadata = {
title: 'A Title',
description: 'Small Description',
authors: [{name: 'I.M. Writer'}],
date: "12-25-97",
}
<ArticleHeader metadata = {metadata}/>
Some cool information goes here...
How would I go about creating a "default template" that allows me to pass data for it to populate itself, and then embed a document in that templet?
no idt template.js is the way here.
if the data can be fetched in bulk, the simplest way is to use usePathname/useParams and get the page data on the client side
if you have many pages or for any reasons you'd like to filter on the server, then use parallel routes https://nextjs.org/docs/app/building-your-application/routing/parallel-routes
if the data can be fetched in bulk, the simplest way is to use usePathname/useParams and get the page data on the client side
"use client";
export function PostMetadata({ allPostData }) {
const { slug } = useParams();
const post = allPostData.find(post => post.slug === slug);
if (!post) notFound();
return <div>{post.title}</div>;
}if you have many pages or for any reasons you'd like to filter on the server, then use parallel routes https://nextjs.org/docs/app/building-your-application/routing/parallel-routes
Painted RedstartOP
im kinda new with react/nextjs but this seems like an antipattern to me? surely there's a way to do this at build time if all the data is static
parallel routes are static. same as the client-side filtering method: the data is also static.
Painted RedstartOP
ah ok, i was under the impression that client side rendering happens during runtime
@joulev no idt template.js is the way here.
if the data can be fetched in bulk, the simplest way is to use usePathname/useParams and get the page data on the client side
tsx
"use client";
export function PostMetadata({ allPostData }) {
const { slug } = useParams();
const post = allPostData.find(post => post.slug === slug);
if (!post) notFound();
return <div>{post.title}</div>;
}
if you have many pages or for any reasons you'd like to filter on the server, then use parallel routes <https://nextjs.org/docs/app/building-your-application/routing/parallel-routes>
Painted RedstartOP
im still struggling a bit with this, im unsure where i would put this and how to accsess all the data since they're spread across the metadata of all my files
@Painted Redstart im still struggling a bit with this, im unsure where i would put this and how to accsess all the data since they're spread across the metadata of all my files
explaining is a bit difficult so i'll show you some examples i wrote:
* client-side filtering: https://github.com/joulev/website/blob/71fd659c7ed5fb16350568458c26ad37449888f8/src/app/(public)/blogs/(posts)/client-components.tsx#L14-L29 and https://github.com/joulev/website/blob/71fd659c7ed5fb16350568458c26ad37449888f8/src/app/(public)/blogs/meta.ts
* parallel routes: https://github.com/joulev/website/commit/9b90009fe54cfb9ad536d1650c00842763961625#diff-3fa0c7a315c667661b762e5d92a02202acc7130d2139d37729f3b161c090be76
* client-side filtering: https://github.com/joulev/website/blob/71fd659c7ed5fb16350568458c26ad37449888f8/src/app/(public)/blogs/(posts)/client-components.tsx#L14-L29 and https://github.com/joulev/website/blob/71fd659c7ed5fb16350568458c26ad37449888f8/src/app/(public)/blogs/meta.ts
* parallel routes: https://github.com/joulev/website/commit/9b90009fe54cfb9ad536d1650c00842763961625#diff-3fa0c7a315c667661b762e5d92a02202acc7130d2139d37729f3b161c090be76
Painted RedstartOP
ah i think im starting to piece this together, im a bit tired tonight and will implement tomorrow, as well as do a little writeup, the examples helped a lot though, thank you
Painted RedstartOP
im having trouble with the client and server renders conflicting, currently, it's structured similar to your blog, joulev, where there is a centralized ledger of metadata in
the relevant bits of
The image on the left is the desired outcome (it appears for a moment) while the one on the right is my current issue, and nextjs gives:
meta-list, which gets passed to each individual article so they can pick out and export their metadata; and which are used by article-content.jsx, which exports <ThisArtcleHeader> (this component is what renders above "subheader 1" and is in layout.jsx:export default function RootLayout({children}) {
return (
<main>
<ThisArticleHeader/>
{children}
</main>
)
}article-content.jsx:"use client";
import { usePathname } from "next/navigation";
import getArticleMetadata from "src/getarticlemeta.mjs";
import ArticleHeader from "components/ArticleHeader";
function getThisMeta() {
const slug = usePathname().split("/").pop()
return getArticleMetadata(slug) // find article metadata by matching against slug in `meta-list`
}
export function ThisArticleHeader() {
const metadata = getThisMeta()
return <ArticleHeader metadata = {metadata}/>
}the relevant bits of
ArticleHeader are://...
let version_number = props.metadata.version_number
let style_symbol
//version numbers obey lexiographic ordering
if(version_number > process.env.KRISTAL_VERSION) {
style_symbol = styles.beta
version_number = version_number + " (BETA)"
}else if(version_number < process.env.KRISTAL_VERSION) {
style_symbol = styles.old
version_number = version_number + " (OLD)"
} else {
style_symbol = styles.stable
}
// ...
let content = <>
<div>
<span className={style_symbol}>{version_number}</span>
//...</>
return <div {...props}>{content}</div>;The image on the left is the desired outcome (it appears for a moment) while the one on the right is my current issue, and nextjs gives:
Warning: Text content did not match. Server: "0.1.0 (OLD)" Client: "0.1.0"Painted RedstartOP
disabling ssr does not help, which makes sense since i think i want the server's render, not the client's. im still confused why they differ at all, a bit more testing makes it seem like the conditional is cached and
else always executes even when the preceeding conditions are met?Painted RedstartOP
Yeah
Painted RedstartOP
yea that fixed the problem completely :P i wouldve never figured that out without your help, thank you!
You’re welcome
@joulev explaining is a bit difficult so i'll show you some examples i wrote:
* client-side filtering: <https://github.com/joulev/website/blob/71fd659c7ed5fb16350568458c26ad37449888f8/src/app/(public)/blogs/(posts)/client-components.tsx#L14-L29> and <https://github.com/joulev/website/blob/71fd659c7ed5fb16350568458c26ad37449888f8/src/app/(public)/blogs/meta.ts>
* parallel routes: <https://github.com/joulev/website/commit/9b90009fe54cfb9ad536d1650c00842763961625#diff-3fa0c7a315c667661b762e5d92a02202acc7130d2139d37729f3b161c090be76>
Painted RedstartOP
oh, and here's a diagram i made while studying your code, i know you said it's a bit difficult to explain so actually mapping out the data flow was very useful. Couldn'tve pieced it together without your example!
Answer