Do not use a Hilla root layout for login pages

I am building an internal tool for our colleagues. Since Vaadin 24.4.x supports the use of Hilla out of the box I have refactored some of the foundational setup to follow the defaults.

In src/main/frontend/@layout.tsx I have defined a drawer supported layout.

import {createMenuItems, useViewConfig} from '@vaadin/hilla-file-router/runtime.js';
import {effect, signal} from '@vaadin/hilla-react-signals';
import {AppLayout, DrawerToggle, Icon, SideNav, SideNavItem} from '@vaadin/react-components';
import {Suspense, useEffect} from 'react';
import {Link, Outlet, useLocation, useNavigate} from 'react-router-dom';

const defaultTitle = document.title;
const documentTitleSignal = signal('');
effect(() => {
    document.title = documentTitleSignal.value;
});

// Publish for Vaadin to use
(window as any).Vaadin.documentTitleSignal = documentTitleSignal;

export default function MainLayout() {
    const currentTitle = useViewConfig()?.title ?? defaultTitle;
    const navigate = useNavigate();
    const location = useLocation();

    useEffect(() => {
        documentTitleSignal.value = currentTitle;
    }, [currentTitle]);

    return (
        <AppLayout primarySection="drawer">
            <div slot="drawer" className="flex flex-col justify-between h-full p-m">
                <header className="flex flex-col gap-m">
                    <Link to={"/"} className={"font-semibold text-l text-header decoration-none"}>FooBar</Link>
                    <SideNav onNavigate={({ path }) => navigate(path!)} location={location}>
                        {createMenuItems().map(({ to, title, icon }) => (
                            <SideNavItem path={to} key={to} className={`item-${to.replace(/^\//, '')}`}>
                                {icon ? <Icon src={icon} slot="prefix"></Icon> : <></>}
                                {title}
                            </SideNavItem>
                        ))}
                    </SideNav>
                </header>
            </div>

            <DrawerToggle slot="navbar" aria-label="Menu toggle"></DrawerToggle>
            <h1 slot="navbar" className="text-l m-0">
                {documentTitleSignal}
            </h1>

            <Suspense>
                <Outlet />
            </Suspense>
        </AppLayout>
    );
}

Now, we have a LoginView that is defined using Flow. That now always uses the React layout I’ve defined. However, I do want my LoginView to use a different layout that is simply a blank page.

When I add an empty LoginLayout (that inherits AppLayout)

@Route
class LoginLayout : AppLayout(){
}

and use that in

@Route("login", layout = LoginLayout::class)
@AnonymousAllowed
class OAuthLoginView : VerticalLayout() {

It also still uses the Hilla @layout.tsx.

Is there anyway to force Flow to not use the default layout defined in my frontend code?

Not sure whether annotation the LoginLayout with @Layout(value="/login") helps or not, but maybe the info in this documentation help.

@Soroosh_Taefi thanks for your reply! I looked up if I could use @Layout and it seems that the documentation on Router Layouts & Nested Router Targets | Routing & Navigation | Flow | Vaadin Docs is actually from one of the 24.5 alpha builds. I will create a GH issue for that, as that is not correct. Or some badge should be shown for the exact version.

To be able to answer if using @Layout might help me, I will do an attempt with a SNAPSHOT version that includes the annotation.

1 Like

You don’t need to use the snapshot, you can also use the latest alpha version: Releases · vaadin/platform · GitHub

It’s much easier.

I have pulled alpha-16, and at least discovered that @Layout does not solve my issue here.

@Route("login", autoLayout = false)
@AnonymousAllowed
class FormLoginView : VerticalLayout(), BeforeEnterObserver {
...
}

I removed @Layout, kept using @Route with the addition of setting the autoLayout to false. But that does not ignore the parent layout defined in src/main/frontend/@layout.tsx.


I also tried a different setup. I added a LoginLayout:

@Layout("/login")
class LoginLayout : Div(), RouterLayout {
    init {
        add(H1("H1 from LoginLayout.kt"))
    }
}

Then the LoginPage uses @Route("login", layout = LoginLayout::class). But that results in the LoginLayout still being wrapped in the client layout:

and I want to disable for the /login route to not use the default root-parent layout.

1 Like

The @Layout on the server doesn’t override the client @layout.tsx there is the ticket Add feature for rendering an FS router view outside the main layout · Issue #2385 · vaadin/hilla · GitHub that would need implementation so this case would work.
For running a @Layout annotated main layout to skip it in the hilla login view the ViewConfig contains the flowLayout that can be set to false to not get a server layout:

export const config: ViewConfig = {
    flowLayout: false,
}
1 Like

As @mgrankvi said, Vaadin does not support this case - exclude Flow route from Hilla’s auto layout. Please give +1 for the mentioned Github issue.
If your requirements allow doing that, you can with Vaadin 24.5 re-implement your main layout in Java/Flow with @Layout and opt-out the login view from auto layout:

@Route(value = "/login", autoLayout = false)
public class LoginView extends VerticalLayout {
    // login page content
}