Repository navigation
Option to generate offline static HTML files usable without server #3825
Description
🚀 Feature
docusaurus build will build a local production version that has to be docusaurus serve'd to be usable. Can we add an option to build an offline static HTML files that are usable completely without any server, so user can just open index.html with a browser to read the whole documentation.
It's about calculating relative (instead of absolute) URLs, and appending "index.html" at the end of the URLs. Algolia search will have to be removed, any online cloud assets will have to be put in local folders.
Have you read the Contributing Guidelines on issues?
Yes
Comment, Motivation, Pitch
What about other static site generators and libraries?
Gatsby, React, etc.'s build all do the similar thing, they all need a server.
Gatsby has this feature request for option to build such offline static HTML site: gatsbyjs/gatsby#4610, which is closed without the issue being solved. Users keep asking for the feature and for reopening the issue. According to one comment, in Gatsby v1 it actually can generate such static site, it is in v2 it doesn't work.
React serves general purpose and Gatsby is made for any website. But Docusaurus, is primarily made for documentation, it may need the feature of the offline version generation more than React and Gatsby do.
PDF and ebook formats
There is already a feature request, #969, that asks for option to create an offline version in PDF format. It is obviously brilliant to be able to make PDF and maybe also EPUB, MOBI, AZW. PDF and these ebook formats may have less security concern than HTML. But the downsides are, it may be a little time-consuming to achieve the PDF feature; those interactive navs and TOCs and colorful website design and layout will have to be removed in PDF and other ebook formats. Offline static HTML is easier to make. If PDF feature is in the long-term plan, then Offline static HTML could be in a shorter-term to-do list.
Compressed web file format
The offline static web files usable without server, could be simply compressed as a zip or in other common archive formats. User will need to uncompress the file and click index.html in the root folder to use it.
They can also be compiled in CHM (Microsoft Compiled HTML Help), problem is it is a bit old and it does not have native support in non-Windows OS. It's a little surprising there's no standard or universally accepted file format similar to CHM. Perhaps it's due to security concerns.
Activity
RDIL commentedon Jan 6, 2021
You can use electron to freeze it I believe
tomchen commentedon Jan 6, 2021
That'd be a super overkill. Even you have just one webpage, Electron will make it 80-100 MB, putting the whole browser rendering and scripting engines in it.
kinzlp commentedon May 13, 2021
vrabota commentedon Jul 15, 2021
slorber commentedon Aug 31, 2021
@ohkimur mentionned he built a postprocessing step to enable support of local browsing, using the
file://protocol.#448 (comment)
That doesn't look like a bad idea to build this as a postprocessing step.
A Docusaurus plugin could be built in userland to solve this problem. Plugins have a
postBuildlifecycle to use for that.Note: such plugin should take into account the
config.trailingSlashoption because output files are not always/path/index.htmlanymore, and can also be/path.htmlslorber commentedon Aug 31, 2021
Note: for some Docusaurus features (particularly SEO metas such as social image, i18n hreflang...), URLs in the HTML files MUST be fully qualified absolute URLs (domain + absolute path).
Building a site for local offline usage does not prevent you from setting site URL and baseUrl in the config file, otherwise, the build output would not be suitable for online hosting.
For these reasons, it's very unlikely we'll add support for using "relative baseUrl" in Docusaurus, such as
baseUrl: '': it would lead to an output that would only be correct for local usage, it's likely users will deploy sites with broken metadata online without noticing the SEO problems.roguexz commentedon Sep 1, 2021
Moving my conversation from #448 to this thread
@ohkimur - your suggestion works for the most part of it, but Webpack configurations are still being difficult to resolve
@slorber - my use case isn't for offline usage. I am trying to put together a simplistic developer workflow which involves publishing documentation to GitHub pages. At my workplace, we are using GitHub enterprise.
The use case is as follows,
Given that
baseUrlneeds to be defined as a fixed path (e.g.,/pages/_GH_ORG_/_GH_REPO_NAME_/), it becomes difficult to view the incoming changes till it has been merged.I understand that this is not how most people work. This is part of an exercise where I am trying to encourage my team to get into the habit of documenting their software. The pandemic has made things worse because reviewing the UI / UX now requires a meeting instead of being able to just view the documentation against their repositories.
Any ideas that you might have to improve this workflow / process are most welcome.
I'm more of a Java/JVM guy .. which isn't helping and making the hacking process that much more challenging. Any help is greatly appreciated.
ohkimur commentedon Sep 2, 2021
@slorber I created the docusaurus-plugin-relative-paths to solve the issue. I used the same post-processing approach using Docusaurus
postBuildlifecycle. 🦖😎slorber commentedon Sep 2, 2021
@roguexz , if you used a modern Jamstack tool like Netlify or Vercel (both much better than GH pages), you'd get a much better experience and all PRs would have a "deploy preview" link that includes the changes from the PR, ensuring they are valid (docusaurus can build and you can check the end result a merge would lead to before doing that merge).
See this Docusaurus PR, the Netlify bot added a link so that the PR can be reviewed more easily: #5462 (comment)
This is very easy to set up.
@ohkimur thanks! hope people will like this solution.
One interesting idea could be to have 2 mods:
buildfilesbuildunchanged, create a copy ofbuild, modify it and generate abuild/site.ziparchive that users could download?ohkimur commentedon Sep 2, 2021
@slorber I think this is a great idea. If you want, you can open a issue here and I will work on it. 🐱👤
69 remaining items
pfdgithub commentedon Jun 3, 2024
Sorry, this comment is not about hash route. It is a further discussion of the following comment.
#448
#3825 (comment)
#3825 (comment)
#3825 (comment)
#3825 (comment)
dingbo8128 commentedon Jul 10, 2024
Awesome work! but it's a pity that some local search plugins are not compatible with this feature now. for example https://github.com/easyops-cn/docusaurus-search-local
slorber commentedon Jul 10, 2024
@dingbo8128 unfortunately all search plugins I know crawl the static HTML files. Since we now emit a single empty HTML file and use client-side JS to display the actual content, it's not possible to crawl the HTML files anymore for search engines to index your content.
The community will have to provide a different implementation for this new hash router mode. Since we can't read the HTML files directly, it will likely require using a headless browser to run the HTML pages and extract the rendered content out of it.
Maybe external search engines like Algolia would keep working, considering they run an external crawler. I don't know, if someone gives it a try I'm curious. Although, it's not ideal since it would require network access to get the search results.
Note that our sitemap does not emit a
sitemap.xmlwhen using the hash router. This is a limitation that we could probably handle if it is helpful to implement a local search plugin for the hash router. However I'm not sure it's a good practice to include URLs with#in a sitemap file. In this casesitemap.xmlis probably useless for search engines 🤷♂️dspatoulas commentedon Jul 25, 2024
I'm running into an issue with the generated links returning a 404. Here's an example from the deployed Docusaurus site after navigating to Blog > Docusaurus 3.4 > Hash Router - Experimental (from right side menu).
https://facebook.github.io/docusaurus/#/blog/releases/3.4%23hash-router---experimental
The link should direct users to the
#hash-router---experimentalsection of the 3.4 release page, but instead returnsPage Not Foundif copy and pasted into another browser tab.slorber commentedon Jul 26, 2024
@dspatoulas how did you obtain that link?
The GitHub UI doesn't show it this way, but your link is:
https://facebook.github.io/docusaurus/#/blog/releases/3.4%23hash-router---experimental/3.4%23hash-router---experimental=> it's using%23instead of#(the encoded version)The same link using
#works:https://facebook.github.io/docusaurus/#/blog/releases/3.4#hash-router---experimental
And afaik nowhere in our UI we use
%23so I wonder how did you get that link in the first place.To be honest I'm surprised in doesn't work in
%23, it should IMHO, but it's likely a bug in React Router v5 hash router impl and I suspect it won't be fixed considering they are working toward v7 stable now.hellfiremaga commentedon Oct 23, 2024
Hello
I have might have a useCase + solution :
In one of my site developped with Docusaurus, I have a documentation which need to be on multiple networks and one among it if really user-right limited (no web server allowed, no third party app installation allowed, etc...). I need to use a baseURL because I have some other documentation on some networks which are separate from my docusaurus project.
So I tried your solution to produce a static website and this solution seems to works in the user-right limited network. If I don't use baseURL, it works perfectly. I tried to use baseURL and here is what seems to change in my opinion :
Didn't notice anything else in the static website. Mine is an simple one so I must skipped some problems, but the ones I had wasn't hard ones.
PS : english is not my native language, sorry if my english is a but rusty
slorber commentedon Oct 23, 2024
@hellfiremaga thanks for sharing your use case but I can't do anything with this unfortunately
This is very vague and I'm not sure you "need" to use a baseURL. Please prove it.
Show me concrete details of your setup, including very concrete examples of the other docs in your "network", how to do open those, what are the concrete browser URLs of each docs sites, what are the exact file system locations of these docs sites.
I'm not even sure what you mean by "network". If there's no web server allowed, there's no network, you open the site locally using the
file://protocol.For these reasons, in its current state, I cannot take your feedback into consideration and decide it's worth it to add support for hash router baseUrl.
hellfiremaga commentedon Oct 24, 2024
@slorber Actually, you're right, I agree with you : I don't really need this features. I was able to make a working static website without it, only by making a few change in my docusaurus configuration - thank you for it actually, a really cool feature ! . But, in my opinion, it don't seems an hard work either to use the base URL. I'll explain a bit more my use case, maybe I'm just using docusaurus wrong or I explained it wrong.
Actually, for professional reasons, I'm using docusaurus for the documentation of an internally developed software (let's call it Msoft). I was inspired by colleagues who already use docusaurus for another software (let's call is Osoft) I'm working with, but don't develop anything in it. I'm sure we will have more project like this in the future, but let's stay on the actual time.
In my company, we have multiple networks (I'm not sure it's the right term, we use "information system") and many of them are disconnected from internet. Here are some examples :
I have laptop computer without any connexion to any network where I develop my docusaurus project for Msoft documentation without any other docusaurus and where I can have a local webserver to host it. BaseURL is not necessary, nor the static website. But the documentation will basically be for my personnal use.
I work in a lab where we have multiple PC on the same network (no internet) and where a limited number of users have access. We already have a docusaurus project hosted on a NAS server with a GitLab and a webserver - the Osoft documentation. This project is the one i'm inspiring on. Everyone has access to Osoft documentation, easy use etc... Since I'm not developper for Osoft, I don't need access to the development of their project but still want to provide the Msoft documentation to everyuser. So we have two docusaurus projects every user need to access but only a few develop on it (and different for each project). We decided to use the baseURL to differentiate the two documentations, with only limited interactions between them.
Note that the Msoft documentation is the same that the one from my laptop computer, so even if I don't need a baseURL on the laptop, I've added one to make it work in the lab without conflicting
I also have a few computers on the company network with internet access, but really limited user-right. Webserver is not allowed, nor security modification, software installation, etc... My need is to provide the Msoft documentation to everyuser from my lab everywhere (and my colleague from Osoft doc should do the same). With your feature, I was able to do a static website which seems to work really fine and allow every user to access our documentation. But it made me change the baseURL from my laptop project for the build purpose, then change it back to still be compatible to the lab version.
Sorry for the size of my explanation, I want you to haven enough details from my use case.
This is why I don't need a baseURL, but I'm pretty sure I will forget to revert the baseURL after a static build and lost time to find this problem. The interraction between docs are not mandatory, if we have two static website for two documentation, it's ok.
For my use case, changing baseURL to an empty one in the begging of the static website build could even do the job if it's an easy modification to you.
So again, I totally agree with you, I don't need to use baseURL, it's only simplier for me if I don't have to make modification when I want to build a static website :)
slorber commentedon Oct 24, 2024
@hellfiremaga thanks for the explanation, but this is still not concrete enough. I don't see any concrete file path or URLs being shared here, as I asked.
When you use the hash router, you don't need to use a baseUrl to avoid conflicts. The conflicts are already avoided by opening different files on the file system:
These won't conflict:
file:///network1/msoft/index.html/#/file:///network1/osoft/index.html/#/file:///network2/msoft/index.html/#/file:///network2/osoft/index.html/#/Using a baseUrl will only lead to a longer URL:
file:///network1/msoft/index.html/#/msoft/file:///network1/osoft/index.html/#/osoft/file:///network2/msoft/index.html/#/msoft/file:///network2/osoft/index.html/#/osoft/There's no real benefit to using hash router + baseUrl, unless proven otherwise.
Please tell me the advantage you get by using
file:///network1/msoft/index.html/#/msoft/instead offile:///network1/msoft/index.html/#/You don't need to "hardcode" the baseurl in your config file, you can pass it dynamically with an env variable for example
baseUrl: process.env.BASE_URLand thenBASE_URL='/msoft/' yarn build. It's ok to build your project in different variants, and you can also use the--configflag for larger config variations.You could have scripts in package.json that enable you to conveniently choose the variant you want to build:
hellfiremaga commentedon Oct 24, 2024
@slorber I guess you found the solution what I was looking for 👍
I agree about the non-benefit of the baseURL in a static docusaurus website for all the reason you have. My point was more about the build and the use of baseURL which "break" the mainpage.
With a simple user POV : he double click the index : "page not found", he will think it's broken. Even if I give him the "good" URL (like "file:///network2/osoft/index.html/#/osoft/"), he will try to get back to the main page by the top left logo at a moment, "page not found", he will think it's broken.
But your solution of using the environment variable is clever. I have to admit I didn't even know we could to such a thing. Thanks a lot for that !
I still think it could be a good idea to ignore the base URL for static site. Or, as you proposed, take is as an error. Other users would have the same problem and, if someone find a real use case, it will still be revertable :)
Oh and thanks for your quick answer and help, highly appreciated :)
slorber commentedon Oct 24, 2024
Thanks
Let's wait for a few more feedbacks. If nobody has a use-case for hash router + baseUrl we can force it to
/automatically and print a warning in that case.jeacott1 commentedon Oct 24, 2024
@hellfiremaga why not differentiate based on file path instead of hash route?
file:///network1/msoft/index.html/#/
file:///network1/osoft/index.html/#/
or, you said there was a webserver and gitlab, so either multiple ports or a reverse proxy I assume?, why not just host your docusaurus as a website?
BigWhiteTiger commentedon Nov 25, 2024
Just a little feedback (sorry if something is totally obvious): hash router has safed my project! It does make the relative paths of my static files work... and it does also fixes the problem named under #10059
Agree that the baseUrl is not needed in combination anymore because of the possibility to use my build directory anywhere on my webserver now nevertheless which subdirectory it is within :)
SeraphCoding commentedon Jul 10, 2025
When using the experimental hash router together with localization, serving a build locally works fine, however, once deployed, the hash location routing breaks. It tries to link the localized directories before the hash.
So if I had a url
https://mysite.com/docs/#/XYZ- using the language switcher to go to e.g. "en" sends it tohttps://mysite.com/docs/index.html#/en/XYZ- which then displays a 404 in the default language.Serving the build locally using the docusaurus CLI (with or without the locale) works fine and also does not add this weird "index.html#" into the middle of the path.
The index.html# shows up - as soon as you browse locally as well though.
There seems to be some discrepancy in how the hash router works when served through docusaurus and when served by a web-server that does not support SPAs or when the build files are browsed locally.
slorber commentedon Jul 11, 2025
@SeraphCoding I'm not surprised this doesn't work well, because when using i18n, we use a baseUrl of
/<locale>/and as explained in past comments, baseUrl doesn't work. I'm not sure how easy this would be to fix but last time I checked it was challenging.The workaround would be to build standalone localized sites one after the other, and not include a versioned dropdown. If it's for offline distribution in a zip file, maybe you can propose localized download links to your users instead of adding all locales to a single zip file? But you can still do so if you really want to, it's just that linking from one locale to another doesn't work great.
This is how the
file://protocol works, we are not responsible for this behavior, browsers are.Yes, serving through a server and browsing files locally are totally different things, that's why it's also challenging to support both in Docusaurus with full feature parity