Skip to content

Deploying SSG site under a subdirectory #581

@Shiphan

Description

@Shiphan

I'm trying to deploy a ssg site to github pages, so it will be served under a subdirectory like this: me.github.io/project-name/index.html
and of course it will be an hydration error since the url is different (/project-name vs /)

I have a workaround by setting the base argument in Router and root in HydrationScripts to /project-name
and then in deploy action, move all files to where they should be

mv target/site/project-name/* target/site/
mv target/site/project-name.html target/site/index.html
rmdir target/site/project-name
# then deploy target/site directory

I don't know if there is already a better way to do this,
If not, I think it would be nice to have an option in Cargo.toml like base-url = "/project-name"

If this won't be too hard to implement, I can try to make a PR for this feature


example:
lib.rs

use leptos::prelude::*;
use leptos_meta::MetaTags;
use leptos_router::{
    SsrMode,
    components::{FlatRoutes, Route, Router},
    path,
    static_routes::StaticRoute,
};

const ROOT: &str = "/project-name";

#[cfg(feature = "frontend")]
#[wasm_bindgen::prelude::wasm_bindgen]
pub fn hydrate() {
    console_error_panic_hook::set_once();

    leptos::mount::hydrate_body(App);
}

pub fn shell(options: LeptosOptions) -> impl IntoView {
    view! {
        <!DOCTYPE html>
        <html lang="en">
            <head>
                <meta charset="utf-8"/>
                <meta name="viewport" content="width=device-width, initial-scale=1"/>
                <AutoReload options=options.clone()/>
                <HydrationScripts options root=ROOT/>
                <MetaTags/>
            </head>
            <body>
                <App/>
            </body>
        </html>
    }
}

#[component]
fn App() -> impl IntoView {
    leptos_meta::provide_meta_context();
    let fallback = || view! { <div><p>"Page not found"</p></div> }.into_view();
    view! {
        <Router base=ROOT>
            <nav>
                <a href=format!("{ROOT}/")>"Home"</a>
                <a href=format!("{ROOT}/abc")>"ABC"</a>
            </nav>
            <div class="main-wrapper">
                <main>
                    <FlatRoutes fallback>
                        <Route
                            path=path!("/")
                            view=Home
                            ssr=SsrMode::Static(StaticRoute::new())
                        />
                        <Route
                            path=path!("/abc")
                            view=Abc
                            ssr=SsrMode::Static(StaticRoute::new())
                        />
                    </FlatRoutes>
                </main>
            </div>
        </Router>
    }
}

#[component]
fn Home() -> impl IntoView {
    let count = RwSignal::new(0);
    view! {
        <button
            on:click=move |_| count.update(|x| *x += 1)
        >
            "Count: "{count}
        </button>
    }
}

#[component]
fn Abc() -> impl IntoView {
    let count = RwSignal::new(0);
    view! {
        <button
            on:click=move |_| count.update(|x| *x += 1)
        >
            "Count in abc: "{count}
        </button>
    }
}

main.rs

#[cfg(feature = "server")]
#[tokio::main]
async fn main() {
    use leptos::prelude::*;
    use leptos_axum::generate_route_list_with_ssg;
    use cargo_leptos_test::shell;

    let conf = get_configuration(None).unwrap();
    let leptos_options = conf.leptos_options;
    // Generate the list of routes in your Leptos App
    let (routes, static_routes) = generate_route_list_with_ssg({
        let leptos_options = leptos_options.clone();
        move || shell(leptos_options.clone())
    });

    static_routes.generate(&leptos_options).await;

    use axum::Router;
    use leptos::logging::log;
    use leptos_axum::LeptosRoutes;

    let addr = leptos_options.site_addr;
    let app = Router::new()
        .leptos_routes(&leptos_options, routes, {
            let leptos_options = leptos_options.clone();
            move || shell(leptos_options.clone())
        })
        .fallback(leptos_axum::file_and_error_handler(shell))
        .with_state(leptos_options);

    // run our app with hyper
    // `axum::Server` is a re-export of `hyper::Server`
    log!("listening on http://{}", &addr);
    let listener = tokio::net::TcpListener::bind(&addr).await.unwrap();
    axum::serve(listener, app.into_make_service())
        .await
        .unwrap();
}

#[cfg(not(feature = "server"))]
fn main() {}

For reference, in solidstart, they have a baseURL field in app.config.ts
https://nitro.build/config#baseurl

import { defineConfig } from "@solidjs/start/config";

export default defineConfig({
	server: {
		baseURL: "/project-name",
		prerender: {
			crawlLinks: true,
		}
	}
});

And in trunk, they have a command line option called --public-url
https://trunkrs.dev/guide/advanced/paths.html

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions