fuma-nama / fumadocs

The beautiful docs framework for Next.js. Alternative to Nextra
https://fumadocs.vercel.app
MIT License
1.51k stars 91 forks source link

Rewrite the docs #374

Closed fuma-nama closed 3 months ago

fuma-nama commented 4 months ago

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

fuma-nama commented 4 months ago

I have improved some sections of the docs, welcome for feedback!

seangray-dev commented 4 months ago

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!

fuma-nama commented 4 months ago

You are looking for this: https://fumadocs-dev.vercel.app/docs/headless/page-conventions#meta

seangray-dev commented 4 months ago

ahh lovely thank you

ederross commented 4 months ago

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:

Screenshot 2024-05-21 at 12 15 56

Can anyone help me?

fuma-nama commented 4 months ago

Read the error message, it tells you your hello.mdx is missing a title in frontmatter.

fuma-nama commented 4 months ago

Also this is not a thread to fix issues, please open a Q&A question in discussion or fire an issue next time.

ederross commented 4 months ago

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

DongLin91 commented 4 months ago

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.

image

image

image

image

can you help me out

fuma-nama commented 4 months ago

Can you open a separate issue instead? This issue is only for suggesting docs changes.

changeelog commented 4 months ago

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:

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!

fuma-nama commented 4 months ago

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.

fuma-nama commented 4 months ago

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.

changeelog commented 4 months ago

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.

nextjs

vitepress

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
fuma-nama commented 4 months ago

I would just write it as text tho, but it's indeed a good suggestion.

fuma-nama commented 4 months ago

I'm working on the docs, feel free to check the preview here: https://fumadocs-dev.vercel.app/docs/ui

fuma-nama commented 4 months ago

I've added a section for writing documents, for beginners who may not know much about programming. Feel free to leave some feedback here

fuma-nama commented 4 months ago

Consider adding icons in the same style for other subsections. Currently only UI has its own icons.

core mdx ui

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

changeelog commented 4 months ago

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

image

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:

image

Source: https://nextjs.org/docs/app/api-reference/create-next-app

image

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)

source

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.

image

However I'd change Enjoy heading to something like Exploring Core Features and leave the cards as they are now!

changeelog commented 4 months ago

1.2. Page Conventions

Change the tagline from "Organize documents with file-system based routing" to " Structuring Your Documentation with Fumadocs".

image

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

image


image

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.

image

Frontmatter: Defining Page Metadata

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!

Key Frontmatter Properties:

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.

image

Organizing with meta.json

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

changeelog commented 4 months ago

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.

Node Types

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"
}

Root Folders and Multiple Page Trees

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.

Icons

Both pages and folders can be assigned icons using the icon property. This property accepts a React element that represents the desired icon.

...

changeelog commented 4 months ago

1.4. Internationalization

Change this paragraph (highlighted on the screen) to something like this (the text under the screenshot)

image

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.

changeelog commented 4 months ago

1.5 Breadcrumb

useBreadcrumb Hook

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.

Usage

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>
  );
};

Parameters

Property Type Description
url string The current page's URL
tree PageTree.Root The website's page tree structure

Return Value

An array of BreadcrumbItem objects representing the path from the root to the current page

Page Tree Structure

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:

changeelog commented 4 months ago

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.

Usage

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>

External Links

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>

Dynamic Links

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.

Props

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.

DynamicLink

The DynamicLink component accepts the same props as the Link component.

Accessibility

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.

Examples

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>
changeelog commented 4 months ago

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.

Usage

  1. Import the necessary components from the fumadocs-core/sidebar module:
import { SidebarProvider, SidebarTrigger, SidebarList } from 'fumadocs-core/sidebar';
  1. Wrap your sidebar content with the SidebarProvider component:
return (
  <SidebarProvider>
    {/* Your sidebar content goes here */}
  </SidebarProvider>
);
  1. Add the SidebarTrigger component to toggle the sidebar:
return (
  <SidebarProvider>
    <SidebarTrigger />
    {/* Your sidebar content goes here */}
  </SidebarProvider>
);
  1. Use the SidebarList component to wrap your sidebar items:
return (
  <SidebarProvider>
    <SidebarTrigger />
    <SidebarList>
      {/* Your sidebar items go here */}
    </SidebarList>
  </SidebarProvider>
);

Components

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.

SidebarTrigger

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.

SidebarList

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.

Data Attributes

Attribute Values Description
data-open true, false Indicates if the sidebar is open.
changeelog commented 4 months ago

1.8 TOC

Table of Contents (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.

Usage

To use the TOC component follow these steps:

  1. Import the necessary component from fumadocs-core/toc:
import { TOCProvider, TOCItem } from 'fumadocs-core/toc';
  1. Wrap your context with the TOCProvider component and pass the toc prop with the table of contents data:
return (
    <TOCProvider toc={yourTableOfContentsData}>
    {/* Your content goes here */}
    </TOCProvider>
);
  1. Use the 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>
);

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.

TOCItem

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.

Hooks

useActiveAnchor

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.

changeelog commented 4 months ago

1.9 Find Neighbours

Reserved

changeelog commented 4 months ago

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.

Usage

To parse the table of contents from a Markdown or MDX content, follow these steps:

  1. Import the getTableOfContents function from fumadocs-core/server:
import { getTableOfContents } from 'fumadocs-core/server';
  1. Call the 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.

Output

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.).

changeelog commented 4 months ago

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.

Usage

To get the last edit time of a file in a GitHub repository follow these steps:

  1. Import the getGithubLastEdit function from fumadocs-core/server:
import { getGithubLastEdit } from 'fumadocs-core/server';
  1. Call the 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.

GitHub Token

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',
      });
fuma-nama commented 4 months ago

This is really too long for this thread, could you open a PR instead? Thanks!

changeelog commented 4 months ago

This is really too long for this thread, could you open a PR instead? Thanks!

Oh, definitely!