1 + 1[1] 2
This guide demonstrates an initial approach to displaying Quarto documents in a NextJS application using iframes. I am writing this post because I couldn’t find any solution to this issue on the interwebs.
While not the final solution, it provides a working foundation for integrating Quarto’s notebooks with NextJS’s modern web framework capabilities. The end goal is to move beyond iframes to a fully integrated solution where Quarto content is parsed into native React components, but this implementation serves as a practical starting point for those looking for a quick and dirty solution
app/
├── blog/
│ ├── [slug]/
│ │ ├── layout.tsx # Right sidebar with blog links
│ │ └── page.tsx # iframe container for Quarto content
│ ├── BlogSummary.tsx # Blog metadata and post info
│ └── page.tsx # Main blog listing page
└── public/
└── blogs/ # Quarto HTML files
Create your Quarto document with embedded resources enabled:
---
title: "Your Blog Title"
embed-resources: true
---Run Quarto’s render command to generate a self-contained HTML file:
quarto render document.qmdPlace the generated HTML in your public folder:
mv document.html public/blogs/export default function BlogLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex h-screen m-0 p-0">
<main className="flex-1 m-0 p-0">
{children}
</main>
<aside className="w-48 bg-white shadow-lg h-screen overflow-y-auto border-l">
<div className="p-3">
<h2 className="text-lg font-semibold mb-3">All Posts</h2>
<nav>
{BlogPosts.map((post) => (
<a key={post.title} href={post.link}>
{post.title}
</a>
))}
</nav>
</div>
</aside>
</div>
);
}interface PageProps {
params: Promise<{
slug: string;
}>;
}
export default async function BlogPost({ params }: PageProps) {
const { slug } = await params;
return (
<div className="w-full h-screen">
<iframe
src={`/blogs/${slug}.html`}
className="w-full h-full border-none"
title="Blog post content"
style={{
minHeight: '100vh',
width: '100%',
margin: 0,
padding: 0,
border: 'none'
}}
/>
</div>
);
}Update BlogSummary.tsx with your post information:
interface BlogPost {
title: string;
description: string;
link: string;
type: 'blog' | 'project';
tags: string[];
featured: string;
image: string;
linkType?: 'email' | 'github' | 'external';
}
export const BlogPosts: BlogPost[] = [
{
title: "Getting Quarto Started in NextJS",
description: "How to integrate Quarto with NextJS",
link: "/blog/quarto-nextjs",
type: "blog",
tags: ["nextjs", "web dev", "quarto"],
featured: 'true',
image: "/favicon.ico"
}
];When you click the Render button a document will be generated that includes both content and the output of embedded code. You can embed code like this:
1 + 1[1] 2
You can add options to executable code like this
[1] 4
The echo: false option disables the printing of code (only output is displayed).
library(ggplot2)
x = seq(1:100)
y = seq(1:100)^2
df = data.frame(x,y)
ggplot(df,aes(x = x, y = y))+geom_point()library(ggplot2)
library(plotly)
Attaching package: 'plotly'
The following object is masked from 'package:ggplot2':
last_plot
The following object is masked from 'package:stats':
filter
The following object is masked from 'package:graphics':
layout
x = seq(1:100)
y = seq(1:100)^2
df = data.frame(x,y)
p = ggplot(df,aes(x = x, y = y))+geom_point()
p = ggplotly(p)
pthis is my \(s\) and this is my \(\phi\)