Skip to content

Option to generate offline static HTML files usable without server #3825

Closed

Description

@tomchen

🚀 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

  1. added
    featureThis is not a bug or issue with Docusausus, per se. It is a feature request for the future.
    status: needs triageThis issue has not been triaged by maintainers
    on Nov 26, 2020
  2. RDIL commented on Jan 6, 2021

    @RDIL
    Contributor

    You can use electron to freeze it I believe

  3. tomchen commented on Jan 6, 2021

    @tomchen
    Author

    You can use electron to freeze it I believe

    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.

  4. kinzlp commented on May 13, 2021

    @kinzlp
  5. vrabota commented on Jul 15, 2021

    @vrabota
  6. slorber commented on Aug 31, 2021

    @slorber
    Collaborator

    @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 postBuild lifecycle to use for that.

    Note: such plugin should take into account the config.trailingSlash option because output files are not always /path/index.html anymore, and can also be /path.html

  7. slorber commented on Aug 31, 2021

    @slorber
    Collaborator

    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.

  8. roguexz commented on Sep 1, 2021

    @roguexz

    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,

    • Developer forks a repository and works on it
    • In the PR they are also contributing the generated site
    • Before merging the generated site, I would like to view the site on their fork

    Given that baseUrl needs 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.

  9. ohkimur commented on Sep 2, 2021

    @ohkimur
    Contributor

    @slorber I created the docusaurus-plugin-relative-paths to solve the issue. I used the same post-processing approach using Docusaurus postBuild lifecycle. 🦖😎

  10. slorber commented on Sep 2, 2021

    @slorber
    Collaborator

    @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:

    • modify build files
    • keep build unchanged, create a copy of build, modify it and generate a build/site.zip archive that users could download?
  11. ohkimur commented on Sep 2, 2021

    @ohkimur
    Contributor

    @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:

    • modify build files
    • keep build unchanged, create a copy of build, modify it and generate a build/site.zip archive that users could download?

    @slorber I think this is a great idea. If you want, you can open a issue here and I will work on it. 🐱‍👤

  12. added
    proposalThis issue is a proposal, usually non-trivial change
    and removed
    featureThis is not a bug or issue with Docusausus, per se. It is a feature request for the future.
    status: needs triageThis issue has not been triaged by maintainers
    on Oct 30, 2021
  13. 69 remaining items

  14. pfdgithub commented on Jun 3, 2024

    @pfdgithub

    @pfdgithub your comment is quite hard for me to understand. So far I am not even sure if it is even related to the current issue because none of the URLs you share have a hash, and the hash part of the URL shouldn't affect routing and redirects in any way. If you want help, make sure it's relevant to the current issue,create a smaller repro, and try to explain better including fully qualified urls because the way you share urls right now does not even make it clear which router config you use.

    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)

  15. dingbo8128 commented on Jul 10, 2024

    @dingbo8128

    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

  16. slorber commented on Jul 10, 2024

    @slorber
    Collaborator

    @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.xml when 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 case sitemap.xml is probably useless for search engines 🤷‍♂️

  17. dspatoulas commented on Jul 25, 2024

    @dspatoulas

    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---experimental section of the 3.4 release page, but instead returns Page Not Found if copy and pasted into another browser tab.

  18. slorber commented on Jul 26, 2024

    @slorber
    Collaborator

    @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 %23 instead 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 %23 so 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.

  19. hellfiremaga commented on Oct 23, 2024

    @hellfiremaga

    e strongly discourage you from using a baseUrl with it. If you have a use-case for a hash baseUrl, please share it, because we might forbid that in the future

    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 :

    • the main page does not load properly (but if you add the baseURL, everything works fine). Every other pages and links works
    • Images can't be found, but if you move them from the build root to a new folder named "baseURL" (so "buildroot/img" should move to "buildroot/baseURL/img"), it seems to work

    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

  20. slorber commented on Oct 23, 2024

    @slorber
    Collaborator

    @hellfiremaga thanks for sharing your use case but I can't do anything with this unfortunately

    I need to use a baseURL because I have some other documentation on some networks which are separate from my docusaurus project.

    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.

  21. hellfiremaga commented on Oct 24, 2024

    @hellfiremaga

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

    @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 :)

  22. slorber commented on Oct 24, 2024

    @slorber
    Collaborator

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

    even if I don't need a baseURL on the laptop, I've added one to make it work in the lab without conflicting

    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 of file:///network1/msoft/index.html/#/

    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.

    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_URL and then BASE_URL='/msoft/' yarn build. It's ok to build your project in different variants, and you can also use the --config flag for larger config variations.

    You could have scripts in package.json that enable you to conveniently choose the variant you want to build:

    "build": "docusaurus build",
    "build:withBaseURL": "BASE_URL='/baseUrl' docusaurus build"
    "build:withDifferentConfig": "docusaurus build --config docusaurus.config.different.ts"
  23. hellfiremaga commented on Oct 24, 2024

    @hellfiremaga

    @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 :)

  24. slorber commented on Oct 24, 2024

    @slorber
    Collaborator

    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.

  25. jeacott1 commented on Oct 24, 2024

    @jeacott1

    @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?

  26. BigWhiteTiger commented on Nov 25, 2024

    @BigWhiteTiger

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

  27. SeraphCoding commented on Jul 10, 2025

    @SeraphCoding

    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 to https://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.

  28. slorber commented on Jul 11, 2025

    @slorber
    Collaborator

    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 to https://mysite.com/docs/index.html#/en/XYZ - which then displays a 404 in the default language.

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

    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.

    This is how the file:// protocol works, we are not responsible for this behavior, browsers are.

    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.

    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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalThis issue is a proposal, usually non-trivial change

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions