Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
a760fa3
npm create fumadocs
YousefED Jan 13, 2026
855e78b
design
YousefED Jan 14, 2026
f3bc1cb
base ui untouched
YousefED Jan 16, 2026
68f3b0c
revert, proper untouched
YousefED Jan 16, 2026
028fcac
add menu groups
YousefED Jan 16, 2026
cf21c99
add content
YousefED Jan 16, 2026
7bbde99
added docs pages and examples
YousefED Jan 17, 2026
dc811a8
eject docs layout
YousefED Jan 17, 2026
1049750
fix docs configuration
YousefED Jan 18, 2026
c543362
add pages and error boundaries
YousefED Jan 19, 2026
7c6fbf2
move pages
YousefED Jan 19, 2026
58bb105
setup sentry
YousefED Jan 19, 2026
0cad360
next config to ts
YousefED Jan 19, 2026
a4d6f30
basic header / footer
YousefED Jan 19, 2026
8ade437
auth
YousefED Jan 19, 2026
9c829f5
add examples
YousefED Jan 19, 2026
eb1136c
llms + readmes
YousefED Jan 19, 2026
1517e3a
add homepage
YousefED Jan 19, 2026
543b692
wip: metadata
YousefED Jan 19, 2026
8ab775c
fix og images and metadata
YousefED Jan 20, 2026
b4cd50c
fix build
YousefED Jan 20, 2026
19d179a
fix lint
YousefED Jan 20, 2026
932b01d
missing files
YousefED Jan 20, 2026
681d132
fix validate links
YousefED Jan 20, 2026
d154526
add email docs
YousefED Jan 20, 2026
9c786f6
migrate directory + fix cardtable
YousefED Jan 20, 2026
761a506
remove generated files from git
YousefED Jan 20, 2026
e5b125b
small cleanup
YousefED Jan 20, 2026
e02c35d
fix / upgrade deps
YousefED Jan 20, 2026
a4a20cd
update lockfile
YousefED Jan 20, 2026
77dc51f
Merge remote-tracking branch 'origin/main' into docs/website-upgrade
YousefED Jan 20, 2026
c0c1cab
merge fix
YousefED Jan 20, 2026
7bac78f
try fix tests
YousefED Jan 20, 2026
5ef2e18
fix
YousefED Jan 20, 2026
17734f3
fix search z-index
YousefED Jan 21, 2026
d2af46b
nav fixes
YousefED Jan 21, 2026
dadb970
fix nav button dark
YousefED Jan 21, 2026
6d92a81
not found
YousefED Jan 21, 2026
051f7cd
fix og
YousefED Jan 21, 2026
ccf80db
misc fixes
YousefED Jan 21, 2026
dc71b22
fix shadcn
YousefED Jan 21, 2026
dde02a0
remove comments
YousefED Jan 21, 2026
b665d93
fix lint
YousefED Jan 21, 2026
8d5e60c
remove fonts + add analytics
YousefED Jan 21, 2026
c2cb344
fix
YousefED Jan 22, 2026
7505fea
fix
YousefED Jan 22, 2026
84f7740
add digital commons
YousefED Jan 22, 2026
8c631d4
i prove play button
YousefED Jan 22, 2026
a024afe
pricing
YousefED Jan 23, 2026
ec417cf
Merge remote-tracking branch 'origin/main' into docs/redesign
YousefED Jan 23, 2026
5df45b9
add demo
YousefED Jan 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ next-env.d.ts

/content/examples/*/*
/components/example/generated/
sqlite.db
sqlite.db
108 changes: 108 additions & 0 deletions docs/app/(home)/_components/BlockCatalog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"use client";
import {
AudioWaveform,
ChevronRight,
Code2,
FileText,
Heading,
Image,
List,
ListOrdered,
ListTodo,
Minus,
Pilcrow,
Puzzle,
Quote,
Table,
Video,
} from "lucide-react";
import React from "react";

const BlockCatalogItem: React.FC<{ name: string; icon: React.ReactNode }> = ({
name,
icon,
}) => (
<div className="group relative flex cursor-default flex-col items-center justify-center overflow-hidden rounded-xl border border-stone-100 bg-white p-3 transition-all duration-300 hover:-translate-y-1 hover:border-purple-200 hover:shadow-xl hover:shadow-purple-500/10">
<div className="absolute inset-0 bg-gradient-to-br from-transparent to-purple-50/50 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
<div className="relative mb-2 flex h-9 w-9 items-center justify-center rounded-lg bg-stone-50 text-stone-400 transition-all duration-300 group-hover:scale-110 group-hover:bg-purple-100 group-hover:text-purple-600">
{icon}
</div>
<span className="relative text-xs font-medium text-stone-500 transition-colors group-hover:text-stone-900">
{name}
</span>
</div>
);

export const BlockCatalog: React.FC = () => {
return (
<section className="relative overflow-hidden bg-gradient-to-b from-stone-50 via-white to-purple-50/30 py-32">
{/* Subtle decorative elements */}
<div className="pointer-events-none absolute inset-0 overflow-hidden">
<div className="absolute -right-20 -top-20 h-96 w-96 rounded-full bg-purple-100/40 blur-3xl" />
<div className="absolute -bottom-20 -left-20 h-96 w-96 rounded-full bg-amber-100/40 blur-3xl" />
<div className="absolute left-1/2 top-1/2 h-[500px] w-[500px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-gradient-to-br from-purple-50/50 to-blue-50/50 blur-3xl" />
</div>

<div className="relative mx-auto max-w-7xl px-6">
<div className="mb-20 text-center">
<div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-2xl border border-purple-100 bg-white/50 text-3xl shadow-sm backdrop-blur-sm">
🧩
</div>
<h2 className="mb-6 font-serif text-4xl text-stone-900 md:text-6xl">
A universe of blocks.
</h2>
<p className="mx-auto max-w-2xl text-xl font-light leading-relaxed text-stone-600">
Every BlockNote document is a collection of blocks—headings, lists,
images, and more. Use the built-in blocks, customize them to fit
your needs, or create entirely new ones.
</p>
</div>

<div className="grid grid-cols-2 gap-3 md:grid-cols-4 lg:grid-cols-6">
<BlockCatalogItem
name="Paragraph"
icon={<Pilcrow className="h-4 w-4" />}
/>
<BlockCatalogItem
name="Headings"
icon={<Heading className="h-4 w-4" />}
/>
<BlockCatalogItem name="List" icon={<List className="h-4 w-4" />} />
<BlockCatalogItem
name="Ordered List"
icon={<ListOrdered className="h-4 w-4" />}
/>
<BlockCatalogItem
name="Checklist"
icon={<ListTodo className="h-4 w-4" />}
/>
<BlockCatalogItem
name="Toggle List"
icon={<ChevronRight className="h-4 w-4" />}
/>
<BlockCatalogItem name="Code" icon={<Code2 className="h-4 w-4" />} />
<BlockCatalogItem name="Quote" icon={<Quote className="h-4 w-4" />} />
<BlockCatalogItem
name="Divider"
icon={<Minus className="h-4 w-4" />}
/>
<BlockCatalogItem name="Table" icon={<Table className="h-4 w-4" />} />
<BlockCatalogItem name="Image" icon={<Image className="h-4 w-4" />} />
<BlockCatalogItem name="Video" icon={<Video className="h-4 w-4" />} />
<BlockCatalogItem
name="Audio"
icon={<AudioWaveform className="h-4 w-4" />}
/>
<BlockCatalogItem
name="File"
icon={<FileText className="h-4 w-4" />}
/>
<BlockCatalogItem
name="Your Own"
icon={<Puzzle className="h-4 w-4" />}
/>
</div>
</div>
</section>
);
};
119 changes: 119 additions & 0 deletions docs/app/(home)/_components/DigitalCommons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"use client";
import Link from "next/link";
import React, { useRef, useState } from "react";

export const DigitalCommons: React.FC = () => {
const videoRef = useRef<HTMLVideoElement>(null);
const [isPlaying, setIsPlaying] = useState(false);

const handlePlayPause = () => {
if (videoRef.current) {
if (isPlaying) {
videoRef.current.pause();
} else {
videoRef.current.play();
}
setIsPlaying(!isPlaying);
}
};

return (
<section className="relative overflow-hidden bg-[#1e1e2f] py-20">
{/* Warm gradient overlay to harmonize with cream hero */}
<div className="pointer-events-none absolute inset-0 bg-gradient-to-br from-amber-900/5 via-transparent to-purple-900/20" />
{/* Top edge gradient for smoother transition */}
<div className="pointer-events-none absolute inset-x-0 top-0 h-24 bg-gradient-to-b from-stone-200/10 to-transparent" />

<div className="relative mx-auto max-w-6xl px-6">
{/* Asymmetric layout: content + video (vertically centered) */}
<div className="flex flex-col items-center gap-12 lg:flex-row lg:items-center lg:gap-16">
{/* Left: Editorial content */}
<div className="flex-1 lg:max-w-md">
{/* Eyebrow with EU flag only */}
<div className="mb-6 flex items-center gap-3">
<span className="text-xl">🇪🇺</span>
<span className="text-xs font-bold uppercase tracking-widest text-amber-200/80">
Digital Commons
</span>
</div>

{/* Headline - editorial style */}
<h2 className="mb-6 font-serif text-3xl leading-tight text-white md:text-4xl">
Three nations choose
<br />
<span className="bg-gradient-to-r from-purple-300 to-blue-300 bg-clip-text text-transparent">
open source
</span>{" "}
to power
<br />
their digital future.
</h2>

{/* Short punchy copy */}
<p className="mb-8 text-base leading-relaxed text-stone-400">
France, Germany, and the Netherlands partner to build{" "}
<strong className="text-white">Docs</strong> — a collaborative
writing tool for thousands of public servants.{" "}
<strong className="text-white">BlockNote is the engine.</strong>
</p>

{/* Compelling social proof - simpler */}
<p className="mb-8 text-sm italic text-stone-500">
"Building Digital Commons means better tools, data sovereignty,
and shared progress."
</p>

{/* CTA */}
<Link
href="https://lasuite.numerique.gouv.fr/en/produits/docs"
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 text-sm font-medium text-purple-300 transition-colors hover:text-purple-200"
>
<span>Watch the story</span>
<span>→</span>
</Link>
</div>

{/* Right: Video - vertically centered */}
<div className="relative flex-1 lg:flex-[1.2]">
{/* Glow effect */}
<div className="absolute -inset-4 rounded-3xl bg-gradient-to-br from-purple-500/15 via-blue-500/10 to-amber-500/5 blur-2xl" />

<div className="relative overflow-hidden rounded-xl border border-white/10 shadow-2xl shadow-purple-900/20">
<video
ref={videoRef}
className="aspect-video w-full cursor-pointer bg-[#2a2a3d] object-cover"
poster="/video/docs-poster.png"
onClick={handlePlayPause}
onEnded={() => setIsPlaying(false)}
playsInline
>
<source src="/video/docs.mp4" type="video/mp4" />
</video>

{/* Play button overlay */}
{!isPlaying && (
<button
onClick={handlePlayPause}
className="absolute inset-0 flex items-center justify-center bg-black/20 transition-all hover:bg-black/30"
aria-label="Play video"
>
<div className="flex h-20 w-20 items-center justify-center rounded-full bg-white shadow-xl transition-transform hover:scale-110">
<svg
className="ml-1 h-8 w-8 text-purple-600"
fill="currentColor"
viewBox="0 0 24 24"
>
<path d="M8 5v14l11-7z" />
</svg>
</div>
</button>
)}
</div>
</div>
</div>
</div>
</section>
);
};
51 changes: 51 additions & 0 deletions docs/app/(home)/_components/FAQ.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from "react";

const faqs = [
{
question: "Isn't it easier to use a Headless editor framework?",
answer:
"There are a number of really powerful headless text editor frameworks available. In fact, BlockNote is built on Prosemirror and TipTap. However, even when using a headless library, it takes several months and requires deep expertise to build a fully-featured editor with a polished UI that your users expect.",
},
{
question: "Is BlockNote ready for production use?",
answer:
"BlockNote is used by dozens of companies in production, ranging from startups to large enterprises and public institutions. Also, we didn't reinvent the wheel. The core editor is built on top of Prosemirror - a battle tested framework that powers software from Atlassian, Gitlab, the New York Times, and many others.",
},
{
question: "Can I add my own extensions to BlockNote?",
answer:
"BlockNote comes with lot of functionality out-of-the-box, but we understand that every use case is different. You can easily customize the built-in UI Components, or create your own custom Blocks, Inline Content, and Styles. If you want to go even further, you can extend the core editor with additional Prosemirror or TipTap plugins.",
},
{
question: "Is BlockNote really free?",
answer:
"100% of BlockNote is open source. We offer consultancy, support services and commercial licenses for specific XL packages to help sustain BlockNote. Explore our pricing page for more details.",
},
];

export const FAQ: React.FC = () => {
return (
<section className="relative overflow-hidden border-t border-stone-100 bg-white py-24">
<div className="mx-auto max-w-5xl px-6">
<div className="mb-16">
<h2 className="font-serif text-5xl font-medium tracking-tight text-stone-900 md:text-6xl">
Questions?
</h2>
</div>

<div className="grid gap-x-12 gap-y-16 md:grid-cols-2">
{faqs.map((faq, index) => (
<div key={index} className="flex flex-col gap-4">
<h3 className="font-sans text-xl font-bold leading-tight text-stone-900">
{faq.question}
</h3>
<p className="font-sans text-base leading-relaxed text-stone-600">
{faq.answer}
</p>
</div>
))}
</div>
</div>
</section>
);
};
108 changes: 108 additions & 0 deletions docs/app/(home)/_components/FeatureAI.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
"use client";
import React, { useState } from "react";
import { FeatureSection } from "./FeatureSection";
import { MockEditor } from "./Shared";

export const FeatureAI: React.FC = () => {
const [activeTab, setActiveTab] = useState<"toolbar" | "models" | "human">(
"toolbar",
);

const tabs = [
{
id: "toolbar",
icon: <span>✨</span>,
label: "AI in the Editor",
description:
"Context-aware completions and edits directly in the document.",
},
{
id: "models",
icon: <span>🔌</span>,
label: "Bring Any Model",
description: "Connect OpenAI, Anthropic, or your own endpoints.",
},
{
id: "human",
icon: <span>🤝</span>,
label: "Human in the Loop",
description: "Users accept, reject, or refine AI suggestions.",
},
];

const getEditorState = () => {
switch (activeTab) {
case "toolbar":
return {
body: (
<>
<span className="rounded bg-purple-100 px-0.5 text-purple-900">
BlockNote is a text editor.
</span>
</>
),
cursorVisible: false,
aiSelection: true,
aiPopupVisible: true,
};
case "models":
return {
body: (
<>
Connect to <strong>OpenAI</strong>, <strong>Anthropic</strong>, or
your own inference endpoints.
</>
),
cursorVisible: false,
aiSelection: false,
aiPopupVisible: false,
};
case "human":
return {
body: (
<>
<div className="mb-3 rounded border border-green-200 bg-green-50 p-2 text-green-800">
<span className="font-medium">AI Suggestion:</span> Improved
version of your text...
</div>
<div className="flex gap-2">
<button className="rounded bg-green-600 px-3 py-1 text-xs text-white">
Accept
</button>
<button className="rounded bg-stone-200 px-3 py-1 text-xs text-stone-600">
Reject
</button>
</div>
</>
),
cursorVisible: false,
aiSelection: false,
aiPopupVisible: false,
};
default:
return {};
}
};

const editorState = getEditorState();

return (
<FeatureSection
title="Built for What's Next."
description="Build the future of document editing. Let users co-author with AI. Connect any model and integrate RAG, tools, and agents—powered by the AI SDK."
tabs={tabs}
activeTabId={activeTab}
onTabChange={(id) => setActiveTab(id as any)}
reverse={true}
>
<MockEditor
title="AI Editor"
body={editorState.body}
cursorVisible={editorState.cursorVisible}
aiSelection={editorState.aiSelection}
aiPopupVisible={editorState.aiPopupVisible}
className="h-[380px] border-none"
/>
</FeatureSection>
);
};
Loading
Loading