Skip to content

chore: migrate to fumadocs and nextjs app router#2606

Open
ArkuVonSymfon wants to merge 142 commits intolangfuse:mainfrom
Altalogy:to-fumadocs
Open

chore: migrate to fumadocs and nextjs app router#2606
ArkuVonSymfon wants to merge 142 commits intolangfuse:mainfrom
Altalogy:to-fumadocs

Conversation

@ArkuVonSymfon
Copy link

Motivation

Migrate the Langfuse docs site from Nextra (Pages Router) to Fumadocs (App Router) while keeping all existing content, styles, and redirects intact.


Description

Framework

  • Replaced nextra to fumadocs
  • Updated next.config.mjs, source.config.ts, tsconfig.json for Fumadocs; upgraded Tailwind v3 → v4
  • Switched production build to Turbopack

App Router

  • New app/ structure: (home), (wide) marketing pages, docs/[[...slug]], blog/, changelog/, and a generic [section]/[[...slug]] catch-all for guides, self-hosting, handbook, users, etc.

Content

  • All content moved from pages/content/<section>/ with meta.json nav files
  • /customers renamed to /users; old URLs covered by permanent redirects

Testing

  • pnpm run build completes without errors — TypeScript, MDX validation, and Turbopack all pass
  • Verified affected pages render correctly in dev server
  • All /customers/*/users/* redirects confirmed in lib/redirects.js

ArkuVonSymfon and others added 30 commits February 18, 2026 18:08
- Switch PostCSS plugin from tailwindcss to @tailwindcss/postcss
- Rewrite style.css for Tailwind v4 syntax (import, @theme, @variant dark)
- Fix fumadocs-ui CSS import order: neutral.css before preset.css
- Fix @apply !important syntax to Tailwind v4 suffix (!), e.g. bg-background!
- Move tailwind.config.js plugins from inside theme to top level (was bug)
- Remove dead nextra-specific CSS from src/overrides.css
- Add turbopack.root to fix Turbopack panic in git worktrees
- Add webpack NormalModuleReplacementPlugin for node: URI scheme
- Add remarkImageOptions: false to prevent remote image fetch at compile time

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: upgrade Tailwind CSS v3→v4 and resolve fumadocs-ui CSS issues
- Wrap NavbarLogo in flex-1 div to balance both sides of the flex layout,
  ensuring NavLinks is truly centered between logo and action buttons
- Update nav link styles to use explicit gray text colors with hover text-only
  transitions (no background on hover) matching Nextra link conventions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix: center navbar and remove hover background from nav links
… fetching in customer components

Turbopack (used by `next dev`) does not apply webpack resolve.alias, so
`nextra/context` could not be resolved and CustomerCarousel/CustomerIndex
returned 0 stories, leaving the homepage slider empty.

- Refactor CustomerCarousel to accept `stories` prop instead of fetching
  internally via `getPagesUnderRoute`
- Refactor CustomerIndex similarly to accept `stories` prop
- CustomerStories (server component) now calls `getPagesForRoute` and
  passes data down to CustomerCarousel
- Add CustomerCarouselWrapper + CustomerIndexWrapper server components for
  MDX pages (enterprise, customers, press) that relied on the old path prop
- Update enterprise.mdx, customers.mdx, press.mdx to use wrapper components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…arousel

The default fumadocs pageSchema strips all unknown fields via Zod strict parsing,
so CustomerCarousel received pages with empty frontMatter — no logos, quotes, or
authors — causing blank carousel cards.

Added customerFrontmatterSchema extending the base schema with all custom fields:
customerLogo, customerLogoDark, customerQuote, quoteAuthor, quoteRole,
quoteCompany, quoteAuthorImage, showInCustomerIndex, date, ogImage, tag, author.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ContentWrapper

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…er values

ogImage and other optional fields can be written as bare 'ogImage:' in YAML,
which parses as null. z.string().optional() only accepts string|undefined, so
swap all custom fields to .nullish() (string|null|undefined).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Changelog.tsx: replace module-level nextra getPagesUnderRoute (which
  returns [] with Turbopack) with getPagesForRoute from @/lib/source
  called inside the component function
- source.config.ts: add changelogFrontmatterSchema extending default
  schema with date, author, ogImage so Fumadocs no longer strips those
  fields from changelog frontmatter
- OpenSource.tsx: duplicate discussions array ([...d, ...d]) in
  ScrollingDiscussions so the marquee loop is truly seamless with no
  visible jump at the end

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…imation

- source.config.ts: add yamlDateField helper using z.union([z.string(),
  z.date().transform(...)]) so unquoted YAML dates (e.g. `date: 2023-07-19`)
  which are parsed as JS Date objects pass schema validation and are
  normalised to YYYY-MM-DD strings; applied to both changelog and customer
  schemas
- shimmer-button.tsx: change animate-spin → animate-spin-custom so the
  spinning conic gradient uses the step-based keyframe defined in style.css
  (--animate-spin-custom / @Keyframes spin-custom) matching the Tailwind v3
  behaviour that was lost when migrating to Tailwind v4

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ailwind v4

Tailwind v4 generates animation utilities as `animation: var(--animate-*)`,
where the custom property value itself contains another var() (e.g. var(--speed),
var(--duration)). This nested var() substitution inside an animation shorthand
is unreliable in some browser/engine combinations and can silently produce an
invalid animation declaration.

Changes:
- style.css: add explicit @layer utilities overrides for all affected
  animation classes (animate-slide, animate-spin-custom, animate-marquee,
  animate-marquee-vertical, animate-border-beam, animate-grid) that use
  direct shorthand values instead of var(--animate-*). These rules come
  after the auto-generated ones in cascade order and win cleanly.

- OpenSource.tsx ScrollingDiscussions: replace animate-marquee-vertical
  class + animationDirection inline-style (which conflict because the
  animation shorthand in the class resets animation-direction) with a
  single inline `animation` shorthand that includes `reverse` directly.
  This is unambiguous and immune to specificity/cascade issues.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
In Nextra (main branch) details/summary elements in MDX content were
styled as polished accordions by Nextra built-in CSS (border, chevron,
hover state). Fumadocs provides no default styling for them, so they
fell back to the browsers unstyled native disclosure widget.

- components/MdxDetails.tsx: new MdxDetails + MdxSummary components
  using Tailwind - rounded border, hidden native marker, ChevronRight
  icon that rotates 90 when open via group-open variant
- mdx-components.tsx: map details to MdxDetails and summary to MdxSummary
- style.css: add padding for content children of details elements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rdions

Two root causes caused FAQ accordions to show browser-default 'Details' label:

1. MdxParagraph wraps summary in a p when MDX sees no blank line between
   closing summary tag and body text. React does not auto-correct invalid DOM
   nesting (unlike the HTML parser), so summary stays inside p inside details
   and the browser never recognises it as the disclosure widget.

2. The BLOCK_TAGS check in MdxParagraph only matches string element types.
   Once summary is mapped to MdxSummary (a function) the check silently falls
   through and a p is emitted instead of a transparent Fragment.

Fixes applied:
- Add hasSummary guard in MdxParagraph: when MdxSummary is detected among
  children, return React Fragment so summary surfaces as a direct child of
  details in the DOM.
- Add blank lines after closing summary tag in all affected content files
  (observability/overview.mdx, security-and-guardrails.mdx,
  llm-as-a-judge.mdx, chatbot-analytics.mdx, testing-llm-applications.mdx)
  so MDX v3 parses summary JSX and body text as separate block elements.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The entire testimonial card is wrapped in an <a> tag so users can click
through to the original post. Without explicit no-underline, the browser
default text-decoration:underline applies to all text inside the anchor
(name, handle, and quote content). Adding no-underline to the anchor
class removes the unwanted underline while keeping the card fully
clickable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…lines

- TestimonialsGrid: constrain platform badge to w-5 h-5 with overflow-hidden,
  flex-center, and proper dark mode bg so the logo sits cleanly inside the circle
- TestimonialsGrid: add no-underline to card anchor (browser default was
  underlining all text inside the link)
- CustomerIndex: reorder Tailwind classes (linter)
- pagination: add list-none to PaginationContent/PaginationItem (linter)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ArkuVonSymfon and others added 26 commits March 5, 2026 10:11
fix: sidebar and mobile menu
fix: hide sidebar modal on blog
fix: logo link and remove search from sidebar on other pages
@vercel
Copy link

vercel bot commented Mar 5, 2026

@ArkuVonSymfon is attempting to deploy a commit to the langfuse Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. enhancement New feature or request labels Mar 5, 2026
@CLAassistant
Copy link

CLAassistant commented Mar 5, 2026

CLA assistant check
All committers have signed the CLA.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XXL This PR changes 1000+ lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants