This is a sleek and efficient web blog project, built with Next.js + MDX + TailwindCSS. You can write your blog posts in Markdown and easily deploy them on Vercel.
It has all the basic functions of a blog website: tag classification, writing, comments, title list, etc.
- Feature Support
- Showcase
- Quick Start
- Data and Configuration
- Packaging and Restoring User Data
- FAQ
- Deployment
- Thanks
- Open Source License
Feature | Support |
---|---|
Math Formula | ✅ |
RSS | ✅ |
Full Text Search | ✅ |
RTL & LTR | Partial |
Code Block | ✅ |
Ready to use | ✅ |
Highly customizable | ✅ |
Blog Comments | ✅ |
SEO | ✅ |
Accessibility | ✅ |
The following blog sites all use this project.
If you want to show your blog here, you can also create a Pull Request to submit your URL.
Before diving into this project, I assume you already have the following knowledge:
- Familiarity with React.js, Next.js, Node.js development, and proficiency in TypeScript.
- Ability to write Markdown documents and HTML documents proficiently.
- Basic knowledge of web development, including deploying web services, SSL, reverse proxy, etc.
The required runtime environment for this project is as follows:
- Node.js v18+
- pnpm v9+
- Chromium-based browser (Chrome, Edge) or Firefox.
I recommend using Visual Studio Code as the development tool.
Execute the following script to install dependencies:
pnpm install
All blog files should be written in Markdown and stored in the /data/posts
directory. However, if you want to create a new blog post, do not directly create a Markdown file in the /data/posts
directory manually! This is because each blog post needs to carry some FrontMatter header information, which should be auto-generated by the program, otherwise, it might cause parsing errors.
You should use a script to create the post.
pnpm run newpost
Then, the program will ask you a series of questions. Simply input your answers according to the prompts, and the program will automatically create a new post file for you and open it.
> lexical-blog@0.1.0 newpost
> node ./scripts/newpost.mjs
? What's the title?
// Required. Enter the title of your article here.
// Recommended no more than 15 words.
? What's the subtitle?
// Optional. Enter the subtitle of your article here.
// Recommended no more than 15 words.
? Assign tags for the posts and separate them with commas.
// Optional. Tag your article and separate the tags with commas.
// E.g., technology,news,programming
// It is recommended that the number of tags should not exceed 4,
// and each tag should be no more than 2 words.
? Do NOT prompt this post? (D:false) No
// Default is no. Do you want to publish this post discreetly?
// If yes, then the article won't be pushed to the homepage or included in RSS after publishing,
// and can only be found in the complete article list.
? Do you want to pin this post? (D:false) No
// Default is no. Do you want to pin this article?
? Do you allow everybody to share this post? (D:true) Yes
// Default is yes. Do you allow others to share this article?
// If no, then copying and sharing via social links will be disabled for this article.
Afterward, the program will display the following message, indicating that the blog file has been created, and you can open and edit it.
Create Post Succeed.
Open the file ./data/posts/2023-12-24-This-is-my-new-post.md to write your blog now.
Some fields, such as summary, need to be filled in by yourself after opening the file.
Open the post file you just created and you can see the following content:
---
title: "xxxxxxx"
subtitle: "xxxxxxx"
summary: ""
coverURL: ""
time: "2023-12-29"
tags: ["xxx","xxx"]
noPrompt: false
pin: false
allowShare: true
---
The existing title
, subtitle
, tags
and other fields are automatically generated for you by the script when you create them. Only summary
and coverURL
fields are still empty. They require you to fill them in manually.
The summary
field is a general summary of the article, which will be displayed in the blog list. It is recommended to be within 50 words. And coverURL
is the cover image of the blog. The network reference link of the image needs to be written, which will be displayed at the top of the post. For optimal presentation, the recommended image aspect ratio is 5:2.
Run the project in development mode.
pnpm run dev
Alternatively, you can use Turbo Build for development mode, which has good performance for hot reloading.
pnpm run dev:turbo
Build the project by running:
pnpm run build
Or use Turbo Build for building the project to enhance performance:
pnpm run build:turbo
The blog's configuration files are located in the ./data
directory, and there are two types, both defined using TypeScript objects. Comments are provided to explain each item's data.
- config.ts: Main configuration information for the website, such as the website title, social accounts, avatar, cover images, etc.
- friends.ts: Used to store friend links.
This project offers a convenient feature for one-click packaging and restoration of user data, including blog files and configurations. This is particularly useful for data migration, backup, or restoration during project upgrades. You can use the following script tool for this purpose:
pnpm run archive
When you run this script, you'll be presented with two options: Pack to archive the user data
and Unpack and restore user data
. These options allow you to package your blog files, configuration files, and other data into a *.tar.gz
file for archiving or to unpack and restore your user data from an archive.
To execute, simply run the script and specify the directory where you want to store the packaged data.
Caution: Before restoring user data, ensure that the current ./data
directory is empty or doesn't contain any critical data you wish to keep. Restoring from an archive will overwrite any existing user data in that directory.
The default layout of this project is LTR. However, this project also supports RTL layout. If you are using Arabic, Persian, Hebrew, etc., please modify _document.tsx manually as following.
Change the Html tag
<Html dir="ltr" lang="en">
{....}
</Html>
Into
<Html dir="rtl" lang="en">
{....}
</Html>
After extensive practical testing, this site primarily uses Chinese and English texts. Therefore, this project employs Fangzheng Xiaobiaosong (for non-commercial use) and Source Han Serif Screen, Source Serif 4、Source Serif KR as font resources. They supports most of Latin Characters, Cylliric Characters and most of common characters, and CJK ideological Characters, Hanguls and Kanas. They display well on both mobile and PC screens, hence are used respectively for official titles and main text.
After careful consideration, this project has adopted next-mdx-remote as the MDX engine. However, considering that this blog is primarily focused on recording text, rather than serving as a documentation-type website, and given the significant differences between MDX and Markdown syntax, this project supports the more commonly used Markdown syntax for content creation. Of course, you can also incorporate HTML snippets within Markdown for more flexible layout options.
Due to the use of the next-mdx-remote
engine in this project, it doesn't allow direct usage of images located in the project directory within Markdown. Therefore, when inserting images, it's recommended to upload them to an image hosting service and then reference the image URLs in the document.
I recommend using PicGO tool to set up your own image hosting. You can click here to view the documentation.
This site supports RSS Feed 2.0 and automatically generates an RSS link with each website build. The articles pushed in the RSS Feed are consistent with the latest articles on the homepage. Of course, you can choose whether to enable it or not through the RSSFeed.enabled
option in ./data/config.ts
.
The /sponsor
page on the blog website is where we showcase our sponsorship channels and provide sponsorship details. You can configure this in the Sponsor
section of ./data/config.ts
. Currently, our blog website supports four sponsorship channels: WeChat Pay, Alipay, Paypal, and Patreon.
Note: WeChat Pay and Alipay are mainly for users in mainland China. If you are not a resident of mainland China or if WeChat Pay and Alipay are not commonly used in your local area, you can ignore the configuration for WeChat Pay and Alipay.
You can find the following configuration options:
Sponsor: {
WechatPayQRCodeContent: "wxp://xxxxxxxxxxxxxxxxx",
AlipayLink: "https://qr.alipay.com/xxxx",
PaypalId: "xxxx",
PatreonId: "xxxx",
...
},
- To set up WeChat Pay as a sponsorship option, first save your WeChat payment QR code. Then use a QR code decoder to read the QR code's content in plain text format. You'll get a string that looks something like
wxp://xxxxxxxxxxxxxxxxx
. Enter this string into theWechatPayQRCodeContent
field. - For Alipay sponsorship, similarly, use a QR code decoder in plain text mode to decode the Alipay payment code. You will obtain a link similar to
https://qr.alipay.com/xxxx
, which you can input. - For Paypal sponsorship, simply input your Paypal ID.
- For Patreon sponsorship, input your Patreon ID.
This website utilizes Giscus as the comment system. For specific configuration instructions, please refer to this link. It requires each commenter to use their own GitHub account. Of course, you can also opt for other comment systems like Disqus.
You can click here to view the relevant information and usage of Giscus.
In this project, we utilize MiniSearch as an in-memory indexed full-text search engine. Taking Shakespeare's complete works as an example, with around 890,000 words, its performance is impressive: the index occupies approximately 10 MB of memory space, and with each query call, the average response time remains below 100 ms.
Note: Currently, full-text search supports only Chinese and languages of the Latin family (English, French, Spanish, etc.). It does not yet support other Asian languages such as Japanese, Korean, Thai, etc. To include support for these languages, the integration of effective tokenizers for these languages is required for indexing. Individuals fluent in these languages are welcome to submit pull requests to assist in completing this task.
For personal blogs, i18n (internationalization) doesn't really serve a practical purpose. Therefore, this project does not support i18n at the moment. If you are a multilingual author and want to distinguish your blogs by language, I suggest adding a tag for the current language in the tags of each article. For instance, if you are a bilingual speaker of Spanish and English, you could add an español
tag under the tags for each Spanish article, and an English
tag for the English blogs.
I recommend deploying this blog website using Vercel. It's popular for being free and powerful.
Click the button below for a quick deployment.
Deploying to your own server can be a bit more complex. We recommend using PM2.js to deploy the website and using reverse proxy tools like Nginx to map the host domain to the local program port.
Additionally, you can use Let's Encrypt + Certbot to configure a free SSL certificate for the website. Click here for more detailed instructions.
This project currently does NOT support deployment to Cloudflare Pages. This is because the full-text search feature in this project does not yet support running in Edge Runtime. This project uses the MDX library to index documents in the dynamic runtime, and the API interfaces of the Edge Runtime and MDX.js library are incompatible.
However, this aspect is still under exploration.
- Thanks for the inspirations from @timlrx 's project tailwind-nextjs-starter-blog, which provides me a conception and technology inspirations to finish this project.
- Thanks for the Font Spilt Tools from @江夏尧 and his project Chinese Web Font (中文网字计划), which provides me a powerful font processing tools, it helped me solve the problem of loading size when loading CJK fonts on the front end.
This project is open source under the MIT License. We welcome any constructive feedback and code contributions.
Note: Please avoid using this project for commercial purposes whenever possible. Some artistic resources, such as third-party icons and fonts referenced in this project, are intended for non-commercial use and may pose legal risks if used commercially.