-
Notifications
You must be signed in to change notification settings - Fork 108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Getting the frontmatter in sveltekit __layout.svelte #313
Comments
Try making what's in context a store and set the value of the store from the mdsvex layout rather than setting context. |
I needed to solve the same problem today and I took a different approach by using This is complicated, so I'll share a code sample later. (Also be aware that using |
with it seems like the method in #122 would be a little more efficient to get the frontmatter out of the file? I tried both approaches and both achieve the goal. it just seems a little awkward, very roundabout way of getting this data that is already loaded...
I want to get the frontmatter in Thanks |
I had the same issue and solved it this way:
I'm just recreating the frontmatter result while adding the whole content to a variable declared in my layouts (fm). You obviously can just select the variables you need but I prefer to have the whole thing. PS: The __layout.svelte should just contain the slot tag as the frontmatter is provided by mdsvex so you need to use these layout files. |
@kasisoft when you say "in your layout add the following", you mean the mdsvex layout, right?
Do you have an example of how to pass |
Yeah I mean the layouts for mdsvex so I'm sorry for commenting as my solution does not apply to __layout.svelte which is the request for this issue (obviously I got carried away here). I'm only using the mdsvex layout whereas __layout.svelte is just a placeholder. |
@kasisoft, no worries, I had the same exact requirement too (passing the entire frontmatter as an object), so I'm sure someone finds it useful too :) |
Alright @Madd0g, I see you have two questions
Dear god I am doing the exact thing as you!! I made some progress How to get metadata to __layoutFor this, I made a file called import { writable } from "svelte/store";
export const meta = writable({}); , then for my <script context="module">
import { meta } from '$lib/stores';
</script>
<script lang="ts">
meta.set(metadata);
</script> , finally in my <script lang="ts">
import { meta } from '$lib/stores';
let metadata;
meta.subscribe(value => {
metadata = value;
});
</script> and boom! but where am I getting my metadata from in How to make a blog from external filesSo far so good! I have an obsidian blog located at What I did was
export function getPosts({ page = 1, limit } = {}) {
let posts = Object.entries(import.meta.globEager('/posts/**/*.md'))
.map(([, post]) => ({ metadata: post.metadata, component: post.default }))
// sort by date
.sort((a, b) => {
return new Date(a.metadata.date).getTime() < new Date(b.metadata.date).getTime() ? 1 : -1
})
console.log(posts)
if (limit) {
return posts.slice((page - 1) * limit, page * limit)
}
return posts
} and in <script context="module">
import { getPosts } from '$lib/getPosts'
export async function load({ page: { params } }) {
const { slug } = params
const post = getPosts().find((post) => slug === post.metadata.slug)
if (!post) {
return {
status: 404,
error: 'Post not found'
}
}
return {
props: {
metadata: post.metadata,
component: post.component
}
}
}
</script> (your final result for so, lets open up slug..... fuck, damn vite protecting us from ourself! but, it's an easy fix. add to svelte.config.js: kit: {
// hydrate the <div id="svelte"> element in src/app.html
target: '#content',
adapter: adapter({
pages: 'public',
assets: 'public'
}),
vite: {
server: {
fs: {
allow: [
// search up for workspace root
// your custom rules
'C:\\Users\\<redacted>\\Personal\\Notes\\blog\\*'
]
}
}
}
} Annnnnd OMG WHOLY SHIT ITS WORKING AAAA
Known issues:
The last 3 might be my problem though Bug: Fixing your CSSadd Catchlet posts;
let purgeNEEDED = true;
export function getPosts(page = 1, limit, purge = false) {
if (purgeNEEDED || purge) {
posts = Object.entries(import.meta.globEager('/posts/**/*.md'))
// format
.map(([, post]) => ({ metadata: post.metadata, component: post.default }))
// sort by date
.sort((a, b) => {
return new Date(a.metadata.date).getTime() < new Date(b.metadata.date).getTime() ? 1 : -1
})
console.log('posts purged and list regenerated.')
if (purgeNEEDED) purgeNEEDED = false;
}
console.log(posts.find((post) => 'Dunkin' === post.metadata.slug))
if (limit) {
return posts.slice((page - 1) * limit, page * limit)
}
return posts;
} |
@boehs - nice! I did approximately the same things to get mine working (even down to symlinking from the obsidian folder, heh). Some of it does feel very dirty, but works and I'm making progress. Weird things:
|
It's certainly dirty. If only I could figure out the 404s, the metadata sometimes working, and unrelated but the navbar sometimes being half blank. I think they are all connected and probably not having anything to do with the blog system but can't for the life of me figure it out. Oh well. I'll give your implementation a look over! |
I feel like sveltekit's Unfortunately, I couldn't find an elegant way to push the frontmatter into stuff. My alternative approach is a fair bit simpler than most of the above and it just uses a regular store. There's a file, import { writable, type Writable } from 'svelte/store';
export const pageMeta: Writable<PageMeta> = writable({}); Next, I have an SEO component that gets called from pretty much every page layout. Here's my entire blog layout file -- most of the actual layout comes from <script>
import Seo from './SEO.svelte';
export let title;
export let description;
export let author;
export let canonical;
export let socialImage;
</script>
<Seo {title} {description} {socialImage} {author} {canonical} />
<slot /> Finally, here are the relevant bits of the SEO component: <script lang="ts">
import { pageMeta } from '../stores/metadata';
export let title: string;
export let description: string;
// ...
$pageMeta = { title, description, author, canonical, socialImage };
</script> And lastly, in my chain of I don't love basically making those globals, but I have good reasons for wanting most of my HTML to be in the relevant layout files and I think this is the best available option. Hope that helps someone. (note: I accidentally posted this on a different issue first... sorry for anyone that's seeing it twice) |
@mvasigh made a great example of how to pull frontmatter into a layout by using endpoints at https://github.com/mvasigh/sveltekit-mdsvex-blog using this __layout.svelte and it’s corresponding [slug].json endpoint. There’s also an example for enumerating a list of posts for a listing page as well (index.svelte and the endpoint posts/index.json.js). These of course still use |
@zmre Is there any way to have mdsvex add this script tag to every page with front matter?
|
Not to my knowledge, but I'm not a mdsvex expert. |
Me and @pngwn had discussion about this today in the Svelte Kit discord and we came to the conclusion that exposing the frontmatter into Making SK <script context="module">
import { load } from "./_load.js"
export { load }
</script> |
Well, damn. This is what I was hoping for, but I recently stumbled on this: Which seems to suggest that |
@zmre it's definitely not set in stone that it's going away, but even if it does there will definitely be a solution for passing data "up" as a replacement. mdsvex should be able to hook into whatever that ends up being. on a mdsvex store, i'm not sure how that would work. would it be possible from an mdsvex perspective to set the value of the store during server rendering? (why should this be a store anyway...?) @pngwn maybe |
I expect you're right about them needing to replace it with something that can serve a similar purpose if it goes away. I think context only works within a group of components in the hierarchy and doesn't work for flowing up to layouts. |
Maybe I missed it, but the const meta = import.meta.glob('my/**/pattern.md', { import: 'metadata', eager: true }) |
hmmm... using `import: 'metadata' does not seem to work for me...keep getting this error:
...dunno why, tho... |
Still a very hacky way to get it but I do import all the markdown files (posts in my case) and the filter by the current URL. <script>
import ProfilePicture from '$lib/components/ProfilePicture.svelte';
import { page } from '$app/stores';
// TODO:
// this is very hacky, but only way for now to get the metadata of the md file directly
// The reason is that the metadata is not available in the layout file so we have to get it from the glob and filter by the current route
// This is a workaround until we have a better solution coming from MDSveX
const posts = import.meta.glob('/src/routes/posts/**/*.md', { eager: true });
const metadata = Object.entries(posts).filter(
(post) => post[0] === '/src/routes' + $page.route.id + '/+page.md'
)[0][1].metadata;
</script>
<div class="mx-auto prose py-10 px-3">
<h1 class="text-4xl font-bold mb-5">
{metadata.title}
</h1>
<!-- Render content of the Markdown file -->
<slot />
</div> in the const config = {
preprocess: [
...
mdsvex({
extensions: ['.md', '.svx'],
layout: {
_: "/src/layouts/default.svelte", // Default layout for markdown files
blog: "/src/layouts/blog.svelte",
}
}),
], |
I tried using an mdsvex layout and not using one, in both cases the default
__layout.svelte
in the folder cannot access the frontmatter from the document.Is there a way to pass it from the mdsvex layout (
blog_layout.svelte
) to the default__layout.svelte
?I'm kind of a beginner in svelte so I don't know if there's an easy svelte-level solution for this. I tried
setContext()
from the mdsvex layout andgetContext
in the default layout, but that didn't work because__layout.svelte
runs first.The text was updated successfully, but these errors were encountered: