Closed fuma-nama closed 3 months ago
I have improved some sections of the docs, welcome for feedback!
Hey @fuma-nama, currently I'm trying to figure out how to change the order of the side-bar navigation, I couldn't find anything in the docs for this.
For example I would like this to be the order:
But currently its being rendered as this by default:
Thanks for your help!
You are looking for this: https://fumadocs-dev.vercel.app/docs/headless/page-conventions#meta
ahh lovely thank you
heey, so... I am seeing the docs and still not figuring out how to use the componets yet haha I am missing something but I can't find it.
When I try to add a component to the mdx file, the erro below appears:
Can anyone help me?
Read the error message, it tells you your hello.mdx
is missing a title in frontmatter.
Also this is not a thread to fix issues, please open a Q&A question in discussion or fire an issue next time.
Also this is not a thread to fix issues, please open a Q&A question in discussion or fire an issue next time.
thanks!! You are awesome!!! I'm love with fumadocs
Hi @fuma-nama ,thank you for your great job. I'm trying to use fumadocs-twoslash. i followed UI-doc .but i just render a raw code block.
can you help me out
Can you open a separate issue instead? This issue is only for suggesting docs changes.
Hey, fuma-nama!
Currently, the Markdown page in the Fumadocs website provides a number of useful plugins for documentation.
My proposal is to rewrite the Markdown page to include the following:
A list of the rehype and remark plugins that are currently used in Fumadocs, along with a concise explanation of what each plugin does. I like how similar thing is implemented on VitePress's website
A section on how to install and configure custom rehype and remark plugins. It's already done admirably. But I just couldn't not include it here, because otherwise the “puzzle” wouldn't be complete
A guide on creating custom rehype and remark plugins from scratch. Docusaurus have some information about this on their website.
In addition to this, a Get Started page could also be slightly re-newed. I like the way it looks now, but at the beginning I'd like to add a section about prerequisites (node version x or higher, etc). It is also worth mentioning the questions that will be asked during installation.
I wanna add some PRs for this, but, damn, the amount of traveling back and forth from point A to point B lately is crazy. I hope to be free by Monday so I can help!
A guide on creating custom rehype and remark plugins from scratch
The other suggestions are great, but maybe this can stay outside of fumadocs docs, remark/rehype has their own guide in writing plugins.
It is also worth mentioning the questions that will be asked during installation.
Could you name a few examples? We currently have mentioned some terminologies which are useful for beginners, but except from the FAQ section, I don't have idea about how a real beginner think about it, and what questions they may ask.
There is indeed a prerequisite of Node.js version (18+) which is added to package.json
, it will be warned by package managers. I'll add this to the guide.
Could you name a few examples?
Oh, sure! Take a look at those screenshots. Such an implementation would allow us to talk a little deeper about the installation process without actually describing it in too much depth. I have attached links to primary sources after the screenshots. Looks incredibly beginner friendly to me.
1) https://nextjs.org/docs/app/api-reference/create-next-app 2) https://vitepress.dev/guide/getting-started
So, if we're talking about Fuma, it could look like this
> npx
> create-fumadocs-app
┌ Create Fumadocs App
│
◆ Project name
│ my-app
◆ Choose a content source
│ ○ Fumadocs MDX
│ ○ Contentlayer
│
◆ Use Tailwind CSS for styling?
│ ○ Yes / ○ No
│
◆ Do you want to install packages automatically? (detected as npm)
│ ○ Yes / ○ No
I would just write it as text tho, but it's indeed a good suggestion.
I'm working on the docs, feel free to check the preview here: https://fumadocs-dev.vercel.app/docs/ui
I've added a section for writing documents, for beginners who may not know much about programming. Feel free to leave some feedback here
Consider adding icons in the same style for other subsections. Currently only UI has its own icons.
For icons sources we could look up here https://github.com/iconify/iconify
Speaking of MDX section, I like those icons:
Speaking of Core section, I like those icons:
I'll write about the navbar thingy later.
Originally posted by @changeelog in https://github.com/fuma-nama/fumadocs/issues/430#issuecomment-2156445068
I'm free from traveling for now, so it's the right time to write all the things I wanted to write. I want to note that packages does not have a readme for the MDX module, and the link in the readme for core module leads to the main site, and not to the site page dedicated to this (as in the case of the UI module). Next, I will try to offer something in more detail for each of the modules.
1. Core
1.1. Quick Start Page
It would be appropriate to include something similar at the top of this page. Maybe rename this sections (on the screenshot) to something like Overwiew
and put the text under the screenshot right there
This guide provides a detailed explanation of how to set up a basic documentation website using Fumadocs Core. It will cover the following topics:
Project Setup: Initiating a new project and installing Fumadocs Core.
Choosing a Content Source: Integrating your documentation content using Fumadocs MDX, Contentlayer or custom sources.
Building the Page Tree: Structuring your documentation for navigation.
Creating Your First MDX Page: Writing content and using Fumadocs components.
Exploring Core Features: A glimpse into search, breadcrumbs, and more.
Further in the text, it seems necessary to delve more into the "formalities" of the installation process. Either clarify the creation of a new directory for the project and go through the installation process in detail, or clarify that the user should select the directory where the project should be installed (cd
command / after the suggestions there're screens from nextjs website displaying "ways" of describing this thing because this is kinda polluted). E.g.:
1. Project Setup
1.1. Create a new project
mkdir my-fumadocs-project
cd my-fumadocs-project
1.2. Initialize your project
Using npm:
npm init -y
Using yarn:
yarn init -y
Using pnpm:
pnpm init
Using bun:
bun init
1.3 Install Fumadocs Core:
npm install fumadocs-core
# or
pnpm add fumadocs-core
# or
yarn add fumadocs-core
# or
bun add fumadocs-core
You will then be asked the following prompts:
> npx
> create-fumadocs-app
┌ Create Fumadocs App
│
◆ Project name
│ my-app
|
◆ Choose a content source
│ ○ Fumadocs MDX
│ ○ Contentlayer
│
◆ Use Tailwind CSS for styling?
│ ○ Yes / ○ No
│
◆ Do you want to install packages automatically? (detected as npm)
│ ○ Yes / ○ No
└
Once you've answered the prompts, a new project will be created with the correct configuration depending on your answers.
This are the examples I've mentioned earlier:
Source: https://nextjs.org/docs/app/api-reference/create-next-app
Source: https://nextjs.org/learn-pages-router/basics/create-nextjs-app/setup
Let's move on to selecting a content source and maybe change the existing thing (on the screenshot) to this (what comes after the screenshot)
Fumadocs Core is content-agnostic. You have the flexibility to use:
Fumadocs MDX (Recommended): Seamless integration with Fumadocs UI and core features.
Contentlayer: Powerful data validation and transformation of your content into type-safe JSON data.
Custom Sources: Adapt any data source to fit your needs.
I like the continuation of this page.
However I'd change Enjoy
heading to something like Exploring Core Features
and leave the cards as they are now!
1.2. Page Conventions
Change the tagline from "Organize documents with file-system based routing" to " Structuring Your Documentation with Fumadocs".
Add a paragraph here of something like this:
Fumadocs leverages a file-system based routing approach to organize your documentation. This convention, shared by content sources like Fumadocs MDX, provides a clear and intuitive structure for managing your pages.
Next, I would like to get rid of longreadness-feeling a little bit, because from the excessive number of headers, webpage seems to be quite large (even tho it isn't). We can combine some headings into a heading. Take a look at the proposed structure after the screenshot
Files (.mdx or .md): Each file represents a single page in your documentation. Folders: Organize related pages into logical groups. Folder names become navigation items by default.
Every page file (.mdx or .md) can include frontmatter to define metadata:
---
title: My Awesome Page // Required: Page title
description: The most insightful content you'll ever read. // Optional: Page description
icon: my-custom-icon // Optional: Icon name (handled by your Source API or UI)
---
Your amazing content starts here!
title (Required): The title displayed in navigation and page headers. description (Optional): A concise summary of the page's content (useful for SEO and previews). icon (Optional): The name of an icon to visually represent the page Custom Properties: Extend your content source to add and utilize your own metadata.
Customize folder behavior and fine-tune navigation using meta.json
files:
1. Customizing Default Folder Display:
{
"title": "Name of Folder",
"defaultOpen": true
}
2. Defining Page Order
{
"title": "Name of Folder",
"pages": ["installation", "quick-start", "..."]
}
3. Creating Separators:
{
"title": "Name of Folder",
"pages": ["---Separator---"]
}
4. Extracting Pages from Other Folders:
{
"pages": ["getting-started", "...tutorials"]
}
5. Adding External Links:
{
"title": "Folder",
"pages": ["index", "[Vercel](https://vercel.com)"]
}
Delete icon thingy and leave the Internationalization section as it is for now I guess
1.3. Page Tree
The page tree defines the hierarchical structure of your documentation site. It encompasses all pages, folders, and separators, providing the blueprint for navigation elements like the sidebar and breadcrumbs.
Important: The page tree is sent to the client. Avoid storing sensitive or large data within it. Unserializable data, such as functions, cannot be included.
The page is composed of three node types:
1. Page
Represents a single page in your documentation.
Property | Type | Default | Description |
---|---|---|---|
type |
"page" |
- | Identifies the node as a page. |
name |
string |
- | The display name of the page. |
url |
string |
- | The URL path to the page. |
external |
boolean |
- | Indicates if the URL is external (optional). |
icon |
ReactElement<any, string \| JSXElementConstructor<any>> |
- | A React element representing the page's icon (optional). |
Example:
{
"type": "page",
"name": "Quick Start",
"url": "/docs"
}
2. Folder
Groups pages and other folders to create a hierarchical structure.
Property | Type | Default | Description |
---|---|---|---|
type |
"folder" |
- | Identifies the node as a folder. |
name |
string |
- | The display name of the folder. |
id |
string |
- | A unique indentifier for the folder. |
root |
boolean |
- | Designates this folder as a root for a separate page tree (optional). |
defaultOpen |
boolean |
- | Determines if the folder is open by default in the navigation (optional). |
index |
Item |
- | Specifies a child node to be displayed as the default view for this folder (optional). |
icon |
ReactElement<any, string \| JSXElementConstructor<any>> |
- | A React element representing the folder's icon (optional). |
chidlren |
Node[] |
- | An array of child nodes (pages, folders, or separators). |
Example:
{
"type": "folder",
"name": "Guide",
"index": {
"type": "page",
...
},
"children": [
...
]
}
3. Separator
Acts as a visual divider between items in the navigation.
Property | Type | Default | Description |
---|---|---|---|
type |
"separator" |
- | Identifies the node as a separator. |
name |
string |
- | The display name of the separator (optional). |
{
"type": "separator",
"name": "Components"
}
You can implement multiple independent page trees within your documentation by utilizing the root
property on folder nodes. The nearest folder with root
set to true
relative to the currently viewed page will act as the root of the displayed page tree.
Both pages and folders can be assigned icons using the icon
property. This property accepts a React element that represents the desired icon.
...
1.4. Internationalization
Change this paragraph (highlighted on the screen) to something like this (the text under the screenshot)
Expand the reach of your documentation by seamlessly integrating multi-lingual support with FumaDocs. This guide will walk you through the process of empowering a global audience to access your content in their preferred language.
The rest of the text seems completely normal. However, we could change:
Define all supported languages in a file. -> Create an i18n.ts
file to centralize your language configuration:
Change your current source configurations. -> Modify the source.ts file to include language awareness
Create the middleware that redirects users when missing locale. -> Create a middleware (middleware.ts
) to ensure users are directed to the appropriate language version:
Create a dynamic route /app/[lang], and move all special files to the folder. -> Create a dynamic route /app/[lang] and migrate special files (e.g., layout.tsx) into this folder:
*Write documents, see Page Conventions to learn how to organize your documents.
Configure i18n on your search solution. For Flexsearch, see Setup I18n.*
TO THIS:
** Organize your multilingual content effectively:
Follow FumaDocs Page Conventions: Adhere to the established structure for organizing your documentation pages. Refer to Next.js I18n Documentation: Leverage Next.js's powerful i18n features for seamless translations.
For Flexsearch, see Setup I18n. -> make it a separate "step" and change it to this -> Configure your search solution (e.g., Flexsearch) to accommodate multiple languages. Refer to the specific setup instructions for your chosen solution.
1.5 Breadcrumb
The useBreadcrumb
hook is a utility function that generates the breadcrumb items based on the current page's URL and the site's page tree structure. It returns an array of BreadcrumbItem
objects representing the path from the root to the current page.
To use the useBreadcrumb
hook in your React components:
import { usePathname } from 'next/navigation';
import { useBreadcrumb } from 'fumadocs-core/breadcrumb';
const MyComponent = ({ tree }) => {
const pathname = usePathname();
const breadcrumbItems = useBreadcrumb(pathname, tree);
return (
<nav>
{breadcrumbItems.map((item, index) => (
<a key={index} href={item.url}>
{item.name}
</a>
))}
</nav>
);
};
Property | Type | Description |
---|---|---|
url |
string |
The current page's URL |
tree |
PageTree.Root |
The website's page tree structure |
An array of BreadcrumbItem
objects representing the path from the root to the current page
The useBreadcrumb
hook relies on a specific page tree structure to generate the breadcrumb items. The page tree is represented by the PageTree
namespace and consists of the following types:
Root
: The root of the page tree, containing an array of Node objects.Node
: A node in the page tree, which can be either a folder or a page.folder
: Represents a folder in the page tree, containing a name, an optional index page, and an array of child Node objects.page
: Represents a single page in the page tree, containing a name and a url.1.6 Link
The Link
component is a wrapper around Next.js's next/link
that provides additional functionality for handling external links in your document. It automatically detects external URLs and uses the appropriate HTML anchor tag (<a>
) instead of the Next.js Link
component. The rel
property is automatically generated for external links.
Using the Link
component is similar to using a regular <a>
tag:
import Link from 'fumadocs-core/link';
<Link href="/docs/components">Click Me</Link>
By default, the Link
component automatically determines if a URL is external. However, you can explicitly force a URL to be treated as external by passing the external
prop:
<Link href="https://example.com" external>External Link</Link>
In Next.js App Router, dynamic hrefs are no longer supported by default. If you need to use dynamic hrefs, you can import the DynamicLink
component from fumadocs-core/dynamic-link
:
import { DynamicLink } from 'fumadocs-core/dynamic-link';
<DynamicLink href="/[lang]/components">Click Me</DynamicLink>
The DynamicLink
component extends the default Link
component and supports dynamic hrefs. It allows you to use placeholders like [lang]
in your href, which will be replaced with the corresponding value from the current route parameters.
Prop | Type | Default | Description |
---|---|---|---|
href |
string |
# |
The URL or path to link to. |
external |
boolean |
false |
Explicitly specify whether the link is external. By default, it is automatically determined based on the href value. |
...props |
object |
- | Additional props to be passed to the underlying <a> or next/link component, such as className , target , rel , etc. |
The DynamicLink
component accepts the same props as the Link
component.
When using the Link
or DynamicLink
component, make sure to provide meaningful text context for the link. This helps users understand the purpose of the link and improves accessibility for screen readers.
Here are a few examples of how to use the Link
and DynamicLink
components:
// Internal link
<Link href="/about">About Us</Link>
// External link
<Link href="https://example.com">Visit Example.com</Link>
// Dynamic link
<DynamicLink href="/blog/[slug]">Read Blog Post</DynamicLink>
1.7 Sidebar
The Sidebar component is a versatile and user-friendly navigation bar that can be placed at the side of the viewport. It automatically handles device resizing and removes the scrollbar when necessary, providing a seamless user experience.
fumadocs-core/sidebar
module:import { SidebarProvider, SidebarTrigger, SidebarList } from 'fumadocs-core/sidebar';
SidebarProvider
component:return (
<SidebarProvider>
{/* Your sidebar content goes here */}
</SidebarProvider>
);
SidebarTrigger
component to toggle the sidebar:return (
<SidebarProvider>
<SidebarTrigger />
{/* Your sidebar content goes here */}
</SidebarProvider>
);
SidebarList
component to wrap your sidebar items:return (
<SidebarProvider>
<SidebarTrigger />
<SidebarList>
{/* Your sidebar items go here */}
</SidebarList>
</SidebarProvider>
);
The SidebarProvider
component is a context provider that manages the state of the sidebar. It should wrap all the other sidebar components.
Prop | Type | Default | Description |
---|---|---|---|
open |
boolean |
- | Specifies whether the sidebar is initially open. |
onOpenChange |
(v: boolean) => void |
- | Callback function invoked when the sidebar state changes. |
children |
ReactNode |
- | The child elements to be rendered within the provider. |
The SidebarTrigger
component is a button that toggles the sidebar when clicked.
Prop | Type | Default | Description |
---|---|---|---|
as |
ElementType |
- | Specifies the HTML element or custom component to render as the trigger. |
The SidebarList
component is a container for the sidebar items. It automatically removes the scrollbar when necessary based on the viewport width.
Prop | Type | Default | Description |
---|---|---|---|
as |
ElementType |
- | Specifies the HTML element or custom component to render as the sidebar list. |
blockScrollingWidth |
number |
- | The viewport width (in pixels) above which scroll blocking is disabled. |
Attribute | Values | Description |
---|---|---|
data-open |
true , false |
Indicates if the sidebar is open. |
1.8 TOC
The Table of Contents (TOC) component provides a convenient way to display a navigable list of anchor links within your documentation. It offers features as active anchor observation and automatic scrolling to the active anchor.
To use the TOC component follow these steps:
fumadocs-core/toc
:import { TOCProvider, TOCItem } from 'fumadocs-core/toc';
toc
prop with the table of contents data:return (
<TOCProvider toc={yourTableOfContentsData}>
{/* Your content goes here */}
</TOCProvider>
);
TOCItem
component to render individual anchor items within the TOC:return (
<TOCProvider toc={yourTableOfContentsData}>
<TOCItem href="/section-1">Section 1</TOCItem>
<TOCItem href="/section-2">Section 2</TOCItem>
{/* More TOCItem components */}
</TOCProvider>
);
The TOCProvider
component serves as a container for all the anchor items. It automatically scrolls to the active anchor by default.
Prop | Type | Default | Description |
---|---|---|---|
toc |
TableOfContents |
- | The table of contents data. |
The TOCItem
component represents an individual anchor item within the table of contents.
Prop | Type | Default | Description |
---|---|---|---|
href |
string |
- | The URL of the anchor item. |
Prop | Type | Default | Description |
---|---|---|---|
data-active |
true , false |
- | Indicates whether the anchor is active. |
The useActiveAnchor
hook is used to determine whether a given anchor item is currently active. It must be called within the TOCProvider
component.
import { useActiveAnchor } from 'fumadocs-core/toc';
const isActive = useActiveAnchor(item.url);
The useActiveAnchor
hook accepts the url
of the anchor item and returns a boolean value indicating whether the anchor is active or not.
1.9 Find Neighbours
Reserved
1.10 Get TOC
The getTableOfContents
function is a utility that parses the table of contents (TOC) from a Markdown or MDX content. It uses the remark
library and the remark-heading
plugin to extract the headings from the content and generate a structured table of contents.
To parse the table of contents from a Markdown or MDX content, follow these steps:
getTableOfContents
function from fumadocs-core/server
:import { getTableOfContents } from 'fumadocs-core/server';
getTableOfContents
function with the Markdown or MDX content as a string:const toc = await getTableOfContents(content);
The getTableOfContents
function returns a promise that resolves to an array of TOCItemType
objects representing the table of contents.
Note: If you're using a Content Management System (CMS), it's recommended to use the API provided by the CMS to retrieve the table of contents instead of parsing it manually.
The getTableOfContents
function returns an array of TOCItemType
objects. Each TOCItemType
object represents a heading in the table of contents and has the following properties:
title
(string): The text content of the heading.
url
(string): The URL fragment that points to the heading in the document.
depth
(number): The nesting level of the heading (e.g., 1 for top-level headings, 2 for subheadings, etc.).
1.11 Last Modified Time
The getGithubLastEdit function is a utility that retrieves the last edit time of a file in a GitHub repository. It uses the GitHub API to fetch the commit information for the specified file and returns the timestamp of the last modification.
To get the last edit time of a file in a GitHub repository follow these steps:
getGithubLastEdit
function from fumadocs-core/server
:import { getGithubLastEdit } from 'fumadocs-core/server';
getGithubLastEdit
function with the requited options:const lastEditTime = await getGithubLastEdit({
owner: 'your-username',
repo: 'your-repository',
path: 'path/to/file.md',
});
The getGithubLastEdit
function returns a promise that resolves to a Date
object representing the last edit time of the file. If the file is not found or has no commit history, the function returns null
.
Options
The getGithubLastEdit
function accepts an object with the following options:
owner
(string, required): The owner of the GitHub repository.
repo
(string, required): The name of the GitHub repository.
path
(string, required): The path to the file within the repository.
token
(string, optional): The GitHub access token for authentication. If provided, it allows for a higher rate limit.
params
(object, optional): Additional query parameters to include in the API request.
options
(object, optional): Custom options to pass to the fetch function.
When making requests to the GitHub API, you may encounter rate limits, especially in development mode. To avoid hitting the rate limit, you can provide a GitHub access token using the token
option. This allows for a higher rate limit.
Learn more about Authenticating to the REST API.
import { getGithubLastEdit } from 'fumadocs-core/server';
const lastEditTime = await getGithubLastEdit({
owner: 'your-username',
repo: 'your-repository',
path: 'path/to/file.md',
token: `Bearer ${process.env.GITHUB_TOKEN}`,
});
Note: If you don't need the last edit time functionality in development mode, you can skip calling the getGithubLastEdit
function to avoid potential rate limit issues.
const lastEditTime =
process.env.NODE_ENV === 'development'
? null
: await getGithubLastEdit({
owner: 'your-username',
repo: 'your-repository',
path: 'path/to/file.md',
});
This is really too long for this thread, could you open a PR instead? Thanks!
This is really too long for this thread, could you open a PR instead? Thanks!
Oh, definitely!
What problem will this feature address?
Few years ago, I built fumadocs. At the time, I wasn't experienced in authoring docs, especially in making tutorial content. This makes the docs of fumadocs became messy and unfriendy for beginners. I think we really need a rewrite of docs.
Describe the solution you'd like
Make all shared pages (e.g. page conventions) to be accessible from all sidebars, and rewrite most of the content.
Describe alternatives you've considered
N/A
Additional context
No response