Open Veetaha opened 2 months ago
Easiest way would be to just use symlinks:
// .vitepress/config.ts
export default defineConfig({
vite: {
resolve: {
preserveSymlinks: true
}
}
})
ln -s v2/guide/ ./guide
ln -s v2/reference ./reference
This won't redirect the page though. But your v2 content will be available without prefix.
Thank you for the suggestion! I've been thinking of using symlinks as well, although using them comes with some drawbacks for the doc-tests automation in my repo, and cross-platformness (if you clone the repo on Windows, symlinks will be just regular files with a path in them).
However, I suppose it'll be easier to do it this way than migrating to Cloudflare pages. This workaround will probably suffice, but I'll keep this issue open for the built-in client-side redirects feature request.
Without symlinks, one of the way is just 404 it and redirect it from there. Because for general stuff it can't be supported properly (for example you want any request to /guide/*
serves v2/guide/index.html
, then for it to work without 404 status in gh pages, you'll need to generate infinite combinations of pathnames as separate html files). docs.rs is also doing server-side redirect.
Example code:
<!-- .vitepress/theme/Layout.vue -->
<script setup lang="ts">
import DefaultTheme from 'vitepress/theme'
import { inBrowser, useData, useRouter } from 'vitepress'
import { watch } from 'vue'
const { page } = useData()
const { go } = useRouter()
const redirects = Object.entries({
'/latest/': '/v2/',
'/guide/': '/v2/guide/',
'/reference/': '/v2/reference/'
})
watch(
() => page.value.isNotFound,
(isNotFound) => {
if (!isNotFound || !inBrowser) return
const redirect = redirects.find(([from]) => window.location.pathname.startsWith(from))
if (!redirect) return
go(redirect[1] + window.location.pathname.slice(redirect[0].length))
},
{ immediate: true }
)
</script>
<template>
<DefaultTheme.Layout />
</template>
// .vitepress/theme/index.ts
import DefaultTheme from 'vitepress/theme'
import Layout from './Layout.vue'
export default { ...DefaultTheme, Layout }
AFAICT using 404.html is the only solution other than maybe re-writing the router to support hash-based navigation. Don't use this if your application is SEO-critical as the page will still be returned with a 404 status code.
Aha, I see so when Github Pages doesn't find the corresponding .html
file for the path it returns a 404 status with 404.html
. And then the client-side JS could redirect the user to the correct page. Yeah, it sounds like a hack, because there is an intermediate 404 status involved.
So now having client-side redirects sounds more like a hack than a feature :eyes:. I suppose such a thing is indeed the responsibility of the server side. Feel free to close this issue if such a hack shouldn't be in scope of VitePress. Although, I think this explanation may be part of the Vitepress docs
https://github.com/vuejs/vitepress/issues/4160#issuecomment-2308509400
Thank you for the code example! I was somehow thinking that Vitepress generated a single HTML for the entire build (since it's an SPA). So I was thinking client-side routing would be an obvious thing to implement in such case.
But I forgot about 404 status code in HTTP response completely, and now I see in the dist
a bunch of different HTML files. I suppose Vitepress lazy-loads them under the hood without the page refresh.
Ah yeah, https://www.youtube.com/watch?v=xXrhg26VCSc (around 46 minute mark)
Without symlinks, one of the way is just 404 it and redirect it from there. Because for general stuff it can't be supported properly (for example you want any request to
/guide/*
servesv2/guide/index.html
, then for it to work without 404 status in gh pages, you'll need to generate infinite combinations of pathnames as separate html files). docs.rs is also doing server-side redirect.Example code:
<!-- .vitepress/theme/Layout.vue --> <script setup lang="ts"> import DefaultTheme from 'vitepress/theme' import { inBrowser, useData, useRouter } from 'vitepress' import { watch } from 'vue' const { page } = useData() const { go } = useRouter() const redirects = Object.entries({ '/latest/': '/v2/', '/guide/': '/v2/guide/', '/reference/': '/v2/reference/' }) watch( () => page.value.isNotFound, (isNotFound) => { if (!isNotFound || !inBrowser) return const redirect = redirects.find(([from]) => window.location.pathname.startsWith(from)) if (!redirect) return go(redirect[1] + window.location.pathname.slice(redirect[0].length)) }, { immediate: true } ) </script> <template> <DefaultTheme.Layout /> </template>
// .vitepress/theme/index.ts import DefaultTheme from 'vitepress/theme' import Layout from './Layout.vue' export default { ...DefaultTheme, Layout }
AFAICT using 404.html is the only solution other than maybe re-writing the router to support hash-based navigation. Don't use this if your application is SEO-critical as the page will still be returned with a 404 status code.
This worked for me! Thanks
Another use case for client-side redirects: I want to reorganize an established docs site. I don't want existing links to break (I can update the ones I control, but can't update people's bookmarks or browser history entries). I'd like to be able to
export default defineConfig({
redirects: {
'/old': '/new',
},
})
Would be wonderful to have a config with a shape similar to Astro's redirects
https://docs.astro.build/en/reference/configuration-reference/#redirects
Hmm, astro supports redirects which can be determined statically. We can also support those. But wildcard redirects cannot be easily supported in a SSG 👀
I've since updated my docs website to a custom domain managed by Cloudflare. I still deploy to Github Pages, but I now have a Cloudflare proxy server in the middle.
Here is how you can achieve the same:
A
records:
AAAA
records and a CNAME
:
CNAME
with a single line of the custom domain name to your /public
dir in Vitepress project dir. I'm not sure if this step is required. It may be required if you are deploying to Github Pages with the old approach (not via the Github Actions artifacts)./discord
redirect to my discord server:
The redirect rules are very flexible, see the docs here.@brc-dd the Cloudflare solution above works for me and I think client-side redirects are more of an antipattern at this point. I'm inclined to close this issue as a "wontfix" unless you think otherwise.
I'm inclined to close this issue as a "wontfix"
I hope this stays open. A solution for redirects which doesn't take more dev chops or budget than the rest of out-of-the-box VitePress would be nice.
//
wildcard redirects cannot be easily supported in a SSG
I haven't looked at Astro's implementation, and don't have a personal need for wildcard redirects currently, and don't need to see a browser wars-style "x has it so y should have it", and conceptually at least Astro's doesn't seem that tricky. ({ 'x/[slug]': 'y/[slug]' }
=> during build, if the output path regex matches y/[slug]
then configure a redirect to it from x/[slug]
).
But hardcoded static redirects would be a great advancement over no client-side redirects.
Is your feature request related to a problem? Please describe.
I'm using Vitepress to document my Rust library called
bon
. I'm hosting it on Github Pages, which is quite a limited hosting provider, but it's a simple one and it's built into Github, so it's very convenient. Github Pages doesn't support server-side redirects, however, VitePress could just do redirects client-side.I'm preparing a
2.0
release of my library. I'm organizing different versions of the docs underv1/
andv2/
path prefixes. However, I'd like to have a special prefix (e.g.latest/
) that would forward to the latest version of the docs, such that I could use this link in my blog posts, for example. The version-specific URLs are used the doc-comments of my library.Or.. ideally, prefix-less paths should forward to the latest version of the docs automatically. A similar thing is implemented on
docs.rs
(the default site that generates docs for Rust libraries):I want to have the same behaviour with Vitepress where
Describe the solution you'd like
There could be client-side redirects config in Vitepress
config.mts
file. For example, an object with glob patterns:(inspired by Cloudflare's routing rules)
Describe alternatives you've considered
Only workarounds:
Additional context
I'm weak at frontend, and especially Vue, but I used other similar frontend frameworks ~6 years ago at university. So I'm seeking for a simple solution to this problem that doesn't involve writing a lot of Vue code or moving to another static page hosting. I adore the current VitePress design and simplicity :heart:
Validations