EasyDocs (ezdoc) turns Markdown content into a pure static blog and documentation site. The generated output contains only HTML, CSS, JavaScript, JSON, and static assets, so it can be deployed to GitHub Pages, a CDN, or any ordinary static-file server.
Demo: NilTor's Blog
Note
Version 1.0 is no longer maintained. The current 2.x line includes the static documentation portal, multilingual/versioned docs, search, SEO metadata, and custom static assets.
- Generate a homepage, blog list, blog pages, documentation pages, documentation homepages, search pages, and an about page.
- Configure multiple documentation projects, languages, and versions in one site.
- Publish multilingual product landing pages with product-specific static assets.
- Blog search, catalog filtering, and archive filtering.
- Markdown rendering with tables, task lists, alerts, citations, figures, auto-links, heading anchors, mathematics, Mermaid/nomnoml diagrams, syntax highlighting, and code-copy controls.
- Local images in blogs and documentation are copied to the generated site.
- Responsive light/dark styling that follows the browser or operating-system preference.
- Optional
Domain-based canonical URLs andsitemap.xmlgeneration. - Optional repository/branch information for “Edit on GitHub” links on documentation pages.
- A
Content/customdirectory for overriding packaged assets or adding custom static files.
dotnet tool install -g Ater.EasyDocsThe installed command is ezdoc.
The user guide is available at dusi.dev/docs/EasyDocs.html, and the source code is hosted at github.com/AterDev/EasyDocs.
The CLI uses Spectre.Console.Cli for structured help and styled output:
ezdoc --help
ezdoc --versioninit and build are the command names in every locale. The CLI selects one localized resource set from the current UI culture, so Chinese systems show Chinese descriptions and English systems show English descriptions. Set DOTNET_CLI_UI_LANGUAGE to zh-CN or en-US to override the detected culture.
Create a workspace and its sample content:
ezdoc init .This creates webinfo.json, preview.cs, and a Content directory containing blogs, docs/example/zh-cn/1.0, docs/example/en-us/1.0, and about.md. The path is optional; without it, the current directory is used. Update webinfo.json after initialization and refer to the official documentation for configuration details.
Build from the configuration file:
ezdoc build .\webinfo.jsonbuild accepts one argument: the path to webinfo.json. Input and output locations are read from ContetPath and OutputPath in that file. The generated site is written to OutputPath (by default ./WebSite).
Preview the output with any static-file server, for example:
npx http-server .\WebSiteAlternatively, use the generated preview.cs with the .NET SDK:
dotnet run .\preview.cs -- .\WebSitewebinfo.json contains site-wide metadata and the documentation catalog:
{
"Name": "Niltor Blog",
"Description": "A personal blog and documentation site",
"AuthorName": "Ater",
"EnableBlog": true,
"ContetPath": "./Content",
"OutputPath": "./WebSite",
"BaseHref": "/blazor-blog/",
"Domain": "https://aterdev.github.io",
"RepositoryUrl": "https://github.com/AterDev/EasyDocs",
"Branch": "main",
"Icon": "favicon.ico",
"Keywords": "docs,blog,EasyDocs",
"DocInfos": [
{
"Name": "EasyDoc",
"Description": "Official documentation",
"Logo": "logo.png",
"Languages": ["zh-cn", "en-us"],
"Versions": ["2.0"]
}
],
"ProductInfos": [
{
"Name": "MyProduct",
"Description": "A sample product with multilingual product documentation.",
"Logo": "logo.svg",
"Languages": ["en-us", "zh-cn"],
"DefaultLanguage": "en-us"
}
]
}Important configuration details:
ContetPathis intentionally spelled this way for compatibility with existing configuration files.BaseHrefmust end with/. Use/when the site is deployed at the domain root.Domainis optional. When set, it is used for page canonical URLs and forsitemap.xml.RepositoryUrlandBranchare optional. When set, they enable documentation edit links.EnableBlogcontrols blog generation. Set it tofalseto omit the Blogs navigation, homepage blog cards, blog pages/data, and blog sitemap entries.Iconis the favicon path relative to the generated site.Logois used for the site/documentation presentation where applicable.- Each
DocInfos[].Name, language, and version must match the corresponding directory names underContent/docs. - Each
ProductInfos[].Nameand language must match the corresponding directory underContent/products/<name>/<language>.DefaultLanguagemust be one of the configured languages and its directory must exist. A productLogois stored at the product directory root.
Content/
├── about.md
├── blogs/
│ ├── first-post.md
│ └── engineering/
│ └── second-post.md
├── custom/
├── products/
│ └── MyProduct/
│ ├── logo.svg
│ ├── privacy-policy.html
│ ├── en-us/
│ │ ├── .order
│ │ └── overview.md
│ └── zh-cn/
│ ├── .order
│ └── overview.md
└── docs/
└── EasyDoc/
├── en-us/2.0/
│ ├── .order
│ └── getting-started.md
└── zh-cn/2.0/
└── 开始使用.md
- Every Markdown file under
blogsbecomes a blog page. Subdirectories become blog catalogs. about.mdbecomesabout.html.- Documentation uses
docs/<name>/<language>/<version>/.... The directory names must be declared inDocInfos. - Products use
products/<name>/<language>/.... The first Markdown file in the default language is used for the product landing page atproducts/<name>.html. - A
.orderfile controls the order of files or directories at that level. Entries use names without the.mdsuffix. - Image files in blogs and docs are copied while the site is generated. There is no required directory name such as
images; put the image beside the Markdown file or in a subdirectory and reference it with the correct relative path. The currently copied local image extensions are.jpg,.jpeg,.png,.gif, and.svg. For an image in the generated documentation homepage, use the./_images/...convention described below. - Relative Markdown links to
.mdfiles are converted to.html; absolute HTTP(S) links are preserved. - Non-Markdown files under a product directory are copied unchanged. For example,
Content/products/MyProduct/privacy-policy.htmlremains available atproducts/MyProduct/privacy-policy.html.
Image paths are resolved relative to the Markdown file. The directory does not have to be named images:
Content/docs/EasyDoc/en-us/2.0/
├── Quick-Start.md
├── logo.svg
└── assets/
└── architecture.png
Both of these references are valid when the files exist at the referenced paths:

For local images in blogs and documentation, keep the file under the corresponding content tree and use one of the supported extensions listed above. Remote http:// and https:// image URLs are kept as-is and are not copied.
Versioned documentation pages support any matching relative image path. The generated documentation homepage at docs/<name>.html is based on the first document, and the current builder rewrites homepage image references only when they start with ./_images. If that image must also appear on the documentation homepage, use this convention:
Content/docs/EasyDoc/en-us/2.0/
├── Quick-Start.md
└── _images/
└── architecture.png
The current release supports file-based customization through Content/custom:
Content/
└── custom/
├── css/
│ ├── app.css
│ ├── docs.css
│ └── markdown.css
├── js/
│ └── site.js
├── index.html
└── images/
└── banner.svg
After all generated pages and packaged assets are created, every file under Content/custom is copied recursively to the same relative path under OutputPath. Existing files are overwritten. This gives you two practical customization options:
- Put a replacement at the same path to override a packaged asset, such as
custom/css/app.css,custom/css/docs.css, orcustom/css/markdown.css. - Put additional images, fonts, JavaScript, or other static files under
custom; reference them from Markdown or from your overridden HTML/CSS.
An arbitrary file such as custom/css/site.css is copied, but it is not automatically linked by the built-in templates. To load a new stylesheet on every page, either override the relevant template output in custom or override one of the built-in CSS files. Because custom files are copied last, a custom index.html, blogs.html, about.html, or another generated page can also replace the generated version.
The built-in templates load these packaged stylesheets:
- Homepage and blog list:
css/app.css - Documentation pages/search pages:
css/app.css,css/docs.css, andcss/markdown.css - Blog content/about pages:
css/app.cssandcss/markdown.css
Custom files are copied only during ezdoc build; editing Content/custom alone does not update an already generated output directory.
The OutputPath directory is the complete static site. Deploy its contents without requiring a .NET runtime. When changing the deployment subpath, update BaseHref and rebuild so generated links, scripts, stylesheets, canonical URLs, and sitemap entries use the correct base path.
Build the CLI project with:
dotnet build .\src\BuildSite\BuildSite.csproj -c ReleaseThe repository also contains the source CSS/JavaScript under WebApp. If those packaged assets are changed, run ./pack.ps1 to refresh src/Share/template/web.zip before packaging the tool.