Skip to content

docs: add the React Views reference with routing and Flow interop - #6616

Merged
peholmst merged 3 commits into
mainfrom
claude/react-views-routing
Nov 10, 2026
Merged

peholmst merged 3 commits into
mainfrom
claude/react-views-routing

Conversation

@peholmst

Copy link
Copy Markdown
Member

Review risk: two-way door. Docs only: four new pages, six removed pages with redirects, and link updates. Reverting brings the old pages back.

Summary

Adds the React Views section to the Flow Reference (articles/flow/react-views/). This is the second step of dissolving the Hilla section into the Flow Reference and Building Apps, after #6611. It moves the routing and Flow integration content. Browser-callable services, forms, security and the rest follow in later pull requests, under the same section.

The old pages were wrong for 25.4 in several places, so the new pages are rewritten rather than moved. Every statement is checked against the main source of the Hilla file router and Flow. This pull request targets main only: v25.3 keeps its Hilla section, so it gets no target/v25.3 label.

What changed

  • New pages in articles/flow/react-views/:

    • index.adoc: what React views are, how to enable them (they require Spring Boot), the frontend files, and how they coexist with Flow views.
    • routing.adoc: the file naming rules, route parameters, layouts, every ViewConfig option, useViewConfig(), createMenuItems(), and the routes.tsx file and its builder methods.
    • flow-interop.adoc: Flow views in the React menu, page titles through documentTitleSignal, Flow layouts around React views, and Flow components in React views with reactElement().
    • component-events.adoc: the Vaadin-specific parts of the old Event Handling page, with its two examples.
  • Corrections compared with the old pages:

    • The hook is useViewConfig(). useRouteMetadata was never a framework API.
    • ViewConfig also has flowLayout and lazy.
    • Menu items with the same order are sorted by path, not by title.
    • The title option doesn't set the browser's page title.
    • The config object is read at build time, so it must be a plain literal.
    • AUTOMATIC sends Flow views to the React menu only when views/@layout.tsx exists.
    • With Spring Security, the default menu access control bean is SpringMenuAccessControl.
    • The generated routes.tsx calls protect().
    • reactElement() must be called while a component renders, because it uses a React hook.
  • Wildcard views: a React wildcard view takes the routes of the Flow views under its directory, so the router shows it instead of them. I confirmed this in a scratch 25.4.0-alpha5 app, for a direct load, a <NavLink> and navigate(), with and without a root layout.

    • routing.adoc warns about it.
    • building-apps/react/navigate.adoc no longer recommends a wildcard view as a 404 page. It points to the Flow not-found view instead, which I checked in the same app.
  • Removed pages, each with a redirect:

    • hilla/guides/routing and hilla/reference/react-router → routing
    • hilla/guides/flow-integration and its stub flow-component-in-hilla → flow-interop#flow-components
    • hilla/guides/event-handling → component-events
    • flow/integrations/hilla → flow-interop

    The two Lit redirects from docs: remove the deprecated Hilla Lit section #6611 that led to the old pages now point at the new ones.

  • Links: updates the links in App Layout, Master-Detail Layout, Modifying the Bootstrap Page, Embedding, Building Apps React Views, and two Hilla pages. Rewords the description of the Flow Reference index.

Test summary

  • npm run check-redirects passes.
  • Every cross reference in the changed files resolves to an existing page and anchor.
  • Vale: no errors or warnings on the added lines. The two remaining suggestions are about the short inline Java snippets.
  • Asciidoctor renders the new pages without warnings.
  • All TSX snippets type-check against the 25.4.0-alpha5 npm packages. The Java snippets compile against the repo's 25.4 classpath, in temporary files that aren't part of this change.
  • No examples changed, so mvn compile and npm run lint weren't needed.
  • I didn't run a full dspublisher build locally.

🤖 Generated with Claude Code

Start the React Views section of the Flow Reference, which replaces the
Hilla Reference step by step. It has an index page, Routing, Flow
Interoperability and Component Events. The pages are checked against
the 25.4 source of the Hilla file router and Flow: useViewConfig()
instead of the nonexistent useRouteMetadata, the flowLayout and lazy
options, menu sorting by path, the build-time config literal, the three
menu population modes, and SpringMenuAccessControl as the Spring
Security default.

Remove the Hilla routing, React Router, Flow Integration, Event
Handling and stub pages, and flow/integrations/hilla, with redirects.

A scratch application showed that a React wildcard view catches the
routes of the Flow views under its directory. Warn about it, and
replace the advice in Navigation and Layouts to use a wildcard view as
a 404 page with the Flow not-found view.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@vaadin-review-bot vaadin-review-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes — left 1 comment.

Finding
⚠️ Component pages don't list events. The events are in the TypeScript API reference that each page links to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Asciidoctor turns ... into an ellipsis character outside code blocks,
so {...name} rendered with an ellipsis and a zero-width space. Escape
the replacement where the name appears in text, a table or a title.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Preview Deployment

This PR has been deployed for preview.

URL: https://docs-preview-pr-6616.fly.dev

Changed pages

Added content is highlighted in green; removed content is marked in red on each page.

Other changed files

  • articles/flow/integrations/hilla.adoc (deleted)
  • articles/hilla/guides/event-handling.adoc (deleted)
  • articles/hilla/guides/flow-component-in-hilla.adoc (deleted)
  • articles/hilla/guides/flow-integration.adoc (deleted)
  • articles/hilla/guides/routing.adoc (deleted)
  • articles/hilla/reference/react-router.adoc (deleted)

Built from bbf21b8

peholmst added a commit that referenced this pull request Nov 10, 2026
Two pages on `v25.3` advise using a React wildcard view as a "page not
found" page. In an application with Flow views, this hides the Flow
views: a wildcard view (`views/{...wildcard}.tsx`, or a view with
`route: '*'` in its `ViewConfig`) takes the place of the Flow fallback
for every path under its directory. The Flow views under that directory
then show the wildcard view instead, for a direct page load, a
`<NavLink>` and `navigate()`, with or without a root `@layout.tsx`. A
custom Flow not-found view works as the replacement.

Changes:

- **Building Apps › React › Navigate, Wildcard Parameters**: brings over
the section from #6616 on `main`. The example is now a
document browser at `views/docs/{...path}.tsx`, followed by a paragraph
that warns about the Flow views and links to [Custom Exception
Handlers](https://vaadin.com/docs/latest/flow/routing/exceptions#custom-exception-handlers).
- **Hilla › Guides › Routing, Adding an Error Page**: replaces the
`route: '*'` example with a pointer to a custom Flow not-found view, and
a warning about wildcard routes in applications with Flow views. The
heading and its anchor are unchanged; nothing else links to it.
- Escapes the ellipsis in the wildcard file names (`{pass:[...]name}`).
Asciidoctor rendered it as an ellipsis character in inline code and in
the block title, as the published page shows today. The block title is
now monospace, which also keeps Vale's ellipsis rule from flagging it.

The behavior was confirmed in a running 25.4.0-alpha5 application while
working on #6616.

`main` gets this fix through #6616, so this pull request is
not for cherry-picking to `main`.

Checks: Vale reports nothing on the changed lines, the redirect check
passes against `v25.3`, and the new link's anchor exists on the
published page.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@peholmst
peholmst merged commit 0e80065 into main Nov 10, 2026
11 checks passed
@peholmst
peholmst deleted the claude/react-views-routing branch November 10, 2026 12:36
peholmst added a commit that referenced this pull request Nov 10, 2026
**Review risk:** two-way door. Docs only: three new pages, four removed
pages with redirects, link updates, and small fixes to three Java
examples. Reverting brings the old pages back.

## Summary
Moves the browser-callable services reference from the Hilla section to
the React Views section of the Flow Reference
(`articles/flow/react-views/browser-callable-services/`). This is the
third step of dissolving the Hilla section, after #6611 and #6616. Type
conversion, nullability and code generation follow in the next pull
request.

The pages are rewritten rather than moved, because the old ones were
wrong for 25.4 in several places. Every statement is checked against
`main` of vaadin/hilla, and the behavior described below was confirmed
in a scratch 25.4.0-alpha5 application. This pull request targets `main`
only, so it gets no `target/v25.3` label.

## What changed
- **New pages:**
- `browser-callable-services/index.adoc`: creating a service, access,
`@Endpoint`, inherited methods, the generated modules, request options,
service URLs, parameter validation, and errors.
- `browser-callable-services/reactive-services.adoc`: `Flux` and
`EndpointSubscription` on the server, and the `Subscription` API in the
browser.
- `browser-callable-services/client.adoc`: the default `ConnectClient`,
a client of your own in `connect-client.ts`, calls without a generated
module, and middleware.
- **Corrections** compared with the old pages, each confirmed in the
scratch application:
- `@EndpointExposed` only controls which methods get TypeScript
functions. A request can call every public method of a service,
including inherited ones, and the access annotations of the service
class apply to them. The old page presented the annotation as protection
against unintentional exposure.
- The errors table gives the status and the error class of each failure.
401 and 403 reject with `UnauthorizedResponseError` and
`ForbiddenResponseError`. A missing service or method, a wrong number of
arguments and an exception other than `EndpointException` reject with
`EndpointResponseError`, not `EndpointError`. The old page named
`EndpointValidationException` as the client-side error and `dev.hilla`
as its package.
- `EndpointException.type` is the class name of the cause when the
exception has one. Field constraints and parameter constraints report
different `parameterName` values, and parameter constraints are checked
only after the objects pass theirs.
- Setting `vaadin.endpoint.prefix` isn't enough: the generated client
hard-codes `connect`, so the calls and subscriptions fail. The pages
describe the supported fix, a `src/main/frontend/connect-client.ts` of
your own, which the generated modules then use. The same file is now the
way to add middleware.
- Subscriptions have no request options, and middleware doesn't see
them. `onError()` gets only `Exception in Flux`. The
`EndpointSubscription` callback runs on cancel and on a lost connection,
not when the `Flux` completes. A subscription whose arguments fail
validation gets neither values nor an error.
- The subscription state is documented as its string values, because
`FluxSubscriptionState` isn't exported. `context()` is described as
Lit-only.
- `@EnableWebMvc` still breaks the services: the call gets the HTML page
of the application.
- **Removed pages, each with a redirect:**
- `hilla/guides/browser-callable-services` → `browser-callable-services`
- `hilla/guides/reactive-services` →
`browser-callable-services/reactive-services`
- `hilla/guides/client-middleware` →
`browser-callable-services/client#middleware`
- `hilla/reference/typescript-client` →
`browser-callable-services/client`

Eight existing redirects that led to these pages
(`/hilla/guides/endpoints`, the `reactive-endpoints` and Lit ones) now
point straight at the new pages.
- **Examples:**
- `pwa/offline/DataService` gets `@AnonymousAllowed`. Without it the
call is denied, so the `EndpointException` example never showed the
message it claims.
  - `CountryService` returned one city fewer than requested.
- Removes the unused `CounterEndpoint` example and the code completion
GIF.
- **Links:** updates the links in ten pages, including Call Java
Services, Server Push, Producing Reactive Streams, Domain Primitives and
the SSO Kit page.

## Test summary
- `npm run check-redirects` passes.
- `mvn spotless:apply` and `mvn compile` pass.
- Every cross reference in the new and changed lines resolves to an
existing page and anchor.
- Vale: no errors or warnings on the added and changed lines. The
remaining suggestions are about inline code snippets.
- Asciidoctor renders the new pages. The only errors are the two
includes of `frontend/generated/*.ts`, which the docs build generates;
the published site renders the same includes.
- All TypeScript snippets type-check against the 25.4.0-alpha5 npm
packages. The inline Java snippets compiled and ran in the scratch
application.
- I didn't run a full dspublisher build locally.

Two behaviors are Hilla issues, and the pages document them as they are:
a subscription gets nothing when its arguments fail validation
(vaadin/hilla#7026), and the generated client ignores
`vaadin.endpoint.prefix` (vaadin/hilla#148).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
peholmst added a commit that referenced this pull request Nov 11, 2026
**Review risk:** two-way door. Docs only: three new pages, five removed
pages with redirects, and link updates. Reverting brings the old pages
back.

## Summary
Moves type conversion, type nullability and code generation from the
Hilla reference to the browser-callable services section of the Flow
Reference (`articles/flow/react-views/browser-callable-services/`). This
is the fourth step of dissolving the Hilla section, after #6611, #6616
and #6618. Form binding follows in the next pull request.

The pages are rewritten rather than moved: the old ones described
Jackson 2, the OpenAPI-based generator and plugin setups that are no
longer accurate. Every statement is checked against `main` of
vaadin/hilla and vaadin/flow. The behavior was confirmed in a scratch
25.4.0-alpha5 application, and every example on the new pages was
generated or type-checked there. This pull request targets `main` only,
so it gets no `target/v25.3` label.

## What changed
- **New pages:**
- `type-conversion.adoc`: the Java-to-TypeScript type mapping, `null`
and `undefined`, numbers, dates and times, enums, objects and Jackson
annotations, single-value and polymorphic types, and customizing the
object mapper.
- `type-nullability.adoc`: the defaults, `@NullMarked` packages, the
recognized annotations, and what the server checks at runtime.
- `code-generation.adoc`: when the code is generated, the generated
files, how the services are found, explicit discovery (keeps the
`#endpoint-discovery` anchor), and parameter names (keeps
`#java-compiler-options`).
- **Corrections** compared with the old pages, each confirmed in the
scratch application:
- Jackson 3 rejects `int` and `long` arguments that are out of range.
The old page said they roll over.
- Unknown properties are ignored. `OffsetDateTime` and `ZonedDateTime`
arguments arrive in UTC. `BigDecimal` and `BigInteger` lose precision in
the browser. Java `NaN` and infinity arrive as strings. The date formats
are those of 25.4.
- The object mapper is customized with a `JacksonObjectMapperFactory`
bean with the `endpointMapperFactory` qualifier, and the
`spring.jackson` properties don't affect it. The generator doesn't use
the custom mapper, so the page warns that changing the properties breaks
the generated types. The old page named a bean qualifier that doesn't
exist.
- The generator honors `@NullMarked` only on packages, not on classes or
sub-packages. JPA `@Id` and `@Version` fields stay nullable. The server
checks return values, but not arguments. The Spring and Hilla
nullability annotations are deprecated, so the page leads with JSpecify.
- Explicit discovery is configured on `vaadin-maven-plugin` (its
`build-frontend` and `generate` goals) and with project properties of
the `com.vaadin` Gradle plugin, not on `hilla-maven-plugin`. It applies
only to builds, not to development mode.
- `-parameters` isn't required: without it, the generated parameters are
called `arg0`, `arg1`, and so on.
- In development mode and in production builds, generation runs only in
projects that use React views. The `generate` goal and the
`hillaGenerate` task don't check for them. No OpenAPI document is
produced any longer, so the OpenAPI appendix and the architecture
section are gone, along with the `tsconfig.json` dump and the list of
core and Pro packages.
- **Removed pages, each with a redirect:**
- `hilla/reference/type-conversion` →
`browser-callable-services/type-conversion`
- `hilla/reference/type-nullability` →
`browser-callable-services/type-nullability`
- `hilla/reference/configuration` and
`hilla/reference/typescript-generator` →
`browser-callable-services/code-generation`
- `hilla/reference/gradle` → `flow/configuration/gradle`. That page now
lists `hillaGenerate`, and the Maven page lists the `generate` goal,
both with a link to Code Generation.

Six existing redirects that led to these pages (`endpoint-generator` and
the Lit ones) now point straight at the new pages.
- **Links:** updates the links in Call Java Services, Domain Primitives,
the upgrading guide, the FAQ and the browser-callable services page. The
nullability tip in Call Java Services now recommends `@NullMarked`
instead of the deprecated `@NonNullApi`.

## Test summary
- `npm run check-redirects` passes.
- Every cross reference in the new and changed lines resolves to an
existing page and anchor.
- Vale: no errors or warnings on the added and changed lines.
- Asciidoctor renders the new pages without errors.
- The examples were generated by the 25.4.0-alpha5 generator in a
scratch application, which produced the TypeScript shown on the pages.
The `ShapeUnion` snippet type-checks against the generated code.
- The Gradle behavior is checked against the plugin source and the
published `com.vaadin` plugin jar, not by running a Gradle build. The
Kotlin nullability support is checked against the source only.
- No Java or TypeScript examples changed. I didn't run a full
dspublisher build locally.

Three Hilla behaviors came up during testing:
- The runtime null check rejects a `null` item even when the item type
is `@Nullable` (vaadin/hilla#7029). The nullability page describes this
as it is.
- `@NullMarked` is read only on packages (vaadin/hilla#6631, already
open). The nullability page says so.
- An invalid `UUID` argument fails with a plain 500 instead of an
`EndpointValidationError` (vaadin/hilla#7030). The pages don't mention
it.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
peholmst added a commit that referenced this pull request Nov 11, 2026
**Review risk:** two-way door. Docs and examples only: six new pages,
nine removed pages with redirects, new and rewritten form examples, and
link updates. Reverting brings the old pages back.

## Summary
Moves the React form binding reference from the Hilla section to the
React Views section of the Flow Reference
(`articles/flow/react-views/forms/`). This is the fifth step of
dissolving the Hilla section, after #6611, #6616, #6618 and #6620.
Security follows in the next pull request.

The pages are rewritten for 25.4, where the generated form models are
`@vaadin/hilla-models` builder constants (vaadin/hilla#6249) and the
model classes are gone (vaadin/hilla#6635). The old pages showed
`AbstractModel`, `ObjectModel` subclasses and `createEmptyValue()`,
which no longer exist. Every statement is checked against `main` of
vaadin/hilla, and every example ran in a scratch 25.4.0-alpha5
application with the models that the generator wrote for the example
classes. This pull request targets `main` only, so it gets no
`target/v25.3` label.

## What changed
- **New pages** in `flow/react-views/forms/`:
- `index.adoc`: form models, binding fields, loading a value,
submitting, and server-side validation.
- `validation.adoc`: the constraints that the browser checks, required
fields, error messages, field and form validators, and errors that a
service reports.
- `form-state.adoc`: the state properties, and changing values from
code.
- `nested-objects-arrays.adoc`: nested objects with `useFormPart()`, and
lists with `useFormArrayPart()`.
- `field-components.adoc`: which Vaadin components `field()` binds, and
the Java types they edit.
- `api.adoc`: the hooks, their results, the validation types, and
`$defaultValue`.
- **Examples:** new compiled examples in `fusion/forms/person` (a person
with an address and phone numbers) and `fusion/forms/signup` (field,
form and server validators). The company office example now sets the
city with `setValue()` of its part, instead of changing `form.value`
directly, which the form doesn't notice. Its package uses `@NullMarked`
instead of the deprecated `@NonNullApi`. The unused `Contact` example
and the Lit-era GIFs are removed.
- **Corrections** compared with the old pages, each confirmed in the
scratch application:
- `submit()` rejects with a `ValidationError` when the validation fails,
so `onClick={submit}` gives an unhandled rejection. The examples catch
it.
- After a submit, the submitted value becomes the default value, even
when the server rejects it, so the form is no longer dirty.
- The browser checks only some constraints. `@PastOrPresent`,
`@FutureOrPresent` and custom constraints are checked only on the
server, and their errors appear after a submit.
- Validators don't run on an empty field that isn't required. A
message's `{min}` placeholders are shown literally unless
`BinderRoot.interpolateMessageCallback` is set.
- Server errors land on the field whose path matches. Errors on method
parameters, and errors that match no field, are only in the
`ValidationError`.
- `List Box` can't be bound: `field()` throws. `Switch` doesn't update
the form value. The old page listed List Box as supported.
- `useFormArrayPart()` needs a non-nullable list, and new items come
from `ItemModel[$defaultValue]`.
- **Removed pages, each with a redirect:** the nine `hilla/guides/forms`
pages go to the matching new pages. `binder` and `binder-load` both go
to the forms index. `form-images` goes to the current uploads page,
which pull request 9 replaces. The ten Lit redirects that led to these
pages now point straight at the new pages.
- **Links:** updates the links in Add a View, Add a Form, Validation,
Domain Primitives, the Auto Form page, the date and time picker pages,
the shared input field partial, Component Events, Code Generation, and
the browser-callable services page. The Domain Primitives link pointed
to an anchor that didn't exist.

## Test summary
- `npm run check-redirects` passes.
- `mvn spotless:apply` and `mvn compile` pass. `npm run lint` passes on
the new and changed examples.
- The examples ran in a scratch 25.4.0-alpha5 application, which
type-checked them against the generated models. The binding of each
component in the Field Components table was checked there, loading and
editing values.
- Every cross reference in the new and changed lines resolves to an
existing page and anchor.
- Vale: no errors or warnings on the added and changed lines.
- Asciidoctor renders the new pages, with all includes and tags
resolved.
- I didn't run a full dspublisher build locally.

Three Hilla behaviors came up during testing, and the pages describe
them as they are:
- `field()` doesn't read the state of a `Switch`, because there's no
field strategy for `vaadin-switch` (vaadin/hilla#7043).
- `useFormArrayPart()` doesn't accept the model of a nullable list
(vaadin/hilla#7044).
- `field()` rejects `List Box`, although lit-form has a strategy for it
(vaadin/hilla#7045).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
peholmst added a commit that referenced this pull request Nov 11, 2026
**Review risk:** two-way door. Docs and examples only: five new
reference pages, ten removed pages with redirects, merges into three
Flow security pages, and link updates. Reverting brings the old pages
back.

## Summary
Moves the security reference for React views from the Hilla section to
the Flow security reference. The new pages are under
`articles/flow/security/react-views/`, including stateless
authentication. This is the sixth step of dissolving the Hilla section,
after #6611, #6616, #6618, #6620 and #6621. Full-stack signals follow in
the next pull request.

The new pages are reference only: what the access rules are, how Vaadin
checks them, and what the APIs do. They link to other reference pages.
The step-by-step setup stays in Building Apps > Secure React Views,
which now links to the reference instead of the reference linking to it.

Every statement is checked against `main` of vaadin/flow and
vaadin/hilla. The behavior is also checked in a scratch 25.4.0-alpha5
application with Spring Security, using curl and a browser. This pull
request targets `main` only, so it gets no `target/v25.3` label.

## What changed
- **New pages** in `flow/security/react-views/`:
- `index.adoc`: the security model, the default access of services,
React views and Flow views, what `VaadinSecurityConfigurer` does for
React views and services, and data kept in the browser.
- `service-access-control.adoc`: the access annotations and their
precedence, role checks, `401` versus `403`, the current user, auditing
denied calls, and CSRF.
- `authentication.adoc`: the React login view, the user information
method, `configureAuth()`, `useAuth()`, `login()` and `logout()`, and
expired sessions.
- `view-access-control.adoc`: the three places where `loginRequired` and
`rolesAllowed` are checked (the router, the menu, page loads), role
names, `hasAccess()`, `protectRoutes()`, and Flow and React views
together.
- **Moved page:**
`flow/security/react-views/stateless-authentication.adoc`, rewritten as
a reference for `VaadinStatelessSecurityConfigurer`, its cookies, and
the contents of the token. It states that stateless authentication is
intended for React views only, and that its behavior with Flow views is
undefined.
- **Merges:**
- `spring/security-configuration.adoc`: the static
`getDefaultHttpSecurityPermitMatcher()` and
`getDefaultWebSecurityIgnoreMatcher()` matchers.
- `protections/vulnerabilities.adoc`: the double-submit cookie check
that services use without Spring Security's CSRF protection, and its
relation to `disable-xsrf-protection`.
- **Examples:** the user information service now allows anonymous calls
and returns `null` when nobody is logged in, using
`AuthenticationContext`. Its record has a `roles` property, which
`configureAuth()` reads by default. A new `ProjectService` shows the
access annotations. The login view, the session-expiration overlay and
the connect client are rewritten. The examples that only the removed
pages used are deleted.
- **Corrections and additions** compared with the old pages, each
confirmed in the scratch application:
- A service without annotations is denied to everyone. Controlling
Service Access and Best Practices also said that it needs only a
logged-in user.
- The session-expiration example said that the failed call continues
after the login. It doesn't: `login()` returns no `token`, so
`InvalidSessionMiddleware` doesn't send the call again, and `login()`
reloads the page at `/`. The new example reloads the current page. A
user information method that allows only logged-in users also opened the
overlay for every visitor who isn't logged in.
- After a session expires, calls to anonymous methods fail with `401` as
well, because the CSRF token belonged to the old session.
- When Spring Security has already rejected an anonymous call, no
`EndpointInvocation` event is published. Spring Security publishes its
own event, with the request as its object. The Auditing section said
that the anonymous user arrives in the `EndpointInvocation` event.
- The old role example used `rolesAllowed: ['ROLE_USER']` with roles
that never contained that name.
- The old pages showed the removed `useRouteMetadata`, the deprecated
`requiresLogin`, and `VaadinRequest.getCurrent().getUserPrincipal()`.
- **Removed pages, each with a redirect:** the ten
`hilla/guides/security` pages. Offline authentication, which was built
on Lit-era APIs, and the best-practices, introduction and
vulnerabilities pages go to the matching Flow pages. The nine Lit
redirects that led to these pages now point straight at the new pages.
- **Links:** Routing, Browser-Callable Services, Client and Security
Configuration now link to the new reference pages instead of the
Building Apps guide. Secure React Views links to the reference. The
full-stack signals page linked to the removed introduction.

## Test summary
- `npm run check-redirects` passes.
- `mvn spotless:apply` and `mvn compile` pass. `npx eslint
frontend/demo/auth` passes.
- The frontend examples type-check in the scratch 25.4.0-alpha5
application against the generated services. The login view and the
session-expiration overlay ran there.
- Checked with curl and in the browser in the scratch application:
  - service calls as an anonymous user, a user, and an administrator;
  - page loads, client-side navigation and menus;
  - expired sessions;
  - audit events;
  - stateless authentication across a server restart;
  - the double-submit cookie without Spring Security.
- Every cross reference in the new and changed lines resolves to an
existing page and anchor.
- Vale: no errors or warnings on the added and changed lines.
- Asciidoctor renders the new and changed pages, with all includes and
tags resolved.
- I didn't run a full dspublisher build locally.

Stateless authentication is documented as intended for applications
whose views are all React views, with undefined behavior for Flow views.
Flow views keep their UI state in the HTTP session in any case.

Behaviors that came up during testing, which the pages describe as they
are:
- On a page load of a React view with `loginRequired: true`, Spring
Security compares `rolesAllowed` with the granted authorities exactly.
The menu and the browser compare differently. `rolesAllowed: ['ADMIN']`
therefore answers `403` to an administrator who reloads the page. View
Access Control documents the three comparisons, and recommends writing
the full authority names, such as `ROLE_ADMIN` (vaadin/flow#26962).
- `InvalidSessionMiddleware` retries a call only when the `LoginResult`
has a `token`, which `login()` never returns (vaadin/hilla#7048).

Not changed here: the role example in Building Apps > Secure React Views
hits the `403` above on reload, and its `getRoles` code doesn't
type-check. That guide gets a separate pull request.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
peholmst added a commit that referenced this pull request Nov 11, 2026
**Review risk:** two-way door. Docs and examples only: one new reference
page, one removed page with a redirect, a new section on Shared Signals,
and link updates. Reverting brings the old page back.

## Summary
Moves full-stack signals from the Hilla guides to the Flow UI State
section, as the new page Shared Signals in React Views
(`flow/ui-state/full-stack-signals`). This is the seventh step of
dissolving the Hilla section, after #6611, #6616, #6618, #6620, #6621
and #6622.

The page is a reference: which shared signal types a service can return,
the client API of the signals, how they connect and synchronize, and how
the access annotations apply. It's rewritten for 25.4, because most of
the old page described an API that no longer exists. Every statement is
checked against `main` of vaadin/hilla and vaadin/flow, and the behavior
is checked in a scratch 25.4.0-alpha5 application with two browser tabs.
This pull request targets `main` only, so it gets no `target/v25.3`
label.

## What changed
- **New page** `flow/ui-state/full-stack-signals.adoc`, marked as a
preview feature behind the `fullstackSignals` feature flag:
- the supported types (`SharedValueSignal`, `SharedNumberSignal`,
`SharedListSignal`) and their TypeScript counterparts;
- returning one instance from the service, and service methods with
parameters;
- creating, connecting and reading the signals in a view, and default
values;
- the members of `ValueSignal`, `NumberSignal` and `ListSignal`, and
operation results;
  - changes made on the server;
  - access control.
- **Shared Signals:** a new Validating Changes section for
`withValidator()` and `CommandValidator`, which the Flow page didn't
cover. The UI State index links to the new page.
- **Feature Flags:** `fullstackSignals` moves to the list of
experimental flags, and the Hilla Feature Flags heading goes away.
- **Examples:** a compiled `MeetingService` and a view that uses a
value, number and list signal, replacing the inline examples.
- **Removed page, with a redirect:** `hilla/guides/full-stack-signals` →
`flow/ui-state/full-stack-signals`.
- **Links:** Type Conversion now links to the new page.
- **Corrections** compared with the old page, each checked in the
scratch application:
- `ValueSignal` has no `replace()` or `update()` anymore
(vaadin/hilla#3500), and `ListSignal` has `insertFirst()` as well.
- The operation-validator API (`withOperationValidator`,
`ValidationResult`, `IncrementOperation`, …) no longer exists. Flow's
shared signals use `withValidator(CommandValidator)`, which is now on
the Shared Signals page.
- `new SharedValueSignal<>(false, Boolean.class)` doesn't compile. The
constructors take an initial value, a type, or both as a
`TypeReference`.
- The list example used the index as the React `key`. Each item signal
has a stable `id`.
- `SharedMapSignal` and `SharedNodeSignal` aren't supported. The
generator produces a plain method for them.

## Test summary
- `npm run check-redirects` passes.
- `mvn spotless:apply` and `mvn compile` pass. `npx eslint
frontend/demo/fusion/signals` passes.
- The example service and view type-check in the scratch 25.4.0-alpha5
application. Two tabs share the topic, the votes, and the agenda items:
adding, checking and removing items all sync.
- Checked with the browser and the generated client:
  - the generated types and default values;
  - write before read;
  - access denied on connecting and on changing;
  - `result` and `error` of a failed change;
- a server-side change, which the connected browsers don't get and a new
connection does (vaadin/hilla#5722);
  - assigning the value of a list signal.
- `withValidator()`, chained validators, child signals, transactions and
`asReadonly()` checked with a small program against the 25.4.0-alpha5
classpath.
- Every cross reference in the new and changed lines resolves.
- Vale: no errors or warnings on the added and changed lines with the
pull request configuration.
- Asciidoctor renders the new and changed pages, with all includes and
tags resolved.
- I didn't run a full dspublisher build locally.

Behaviors that came up during testing, which the page describes as they
are:
- Changes that server-side code makes to a shared signal don't reach the
connected browsers, only new connections (vaadin/hilla#5722).
- When a change fails, its `result` promise doesn't settle; the signal's
`error` holds the error. Related to vaadin/hilla#3967.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
peholmst added a commit that referenced this pull request Nov 11, 2026
**Review risk:** two-way door. Docs and examples only: one new page, two
rewritten or extended pages, five removed pages with redirects, and link
and guideline updates. Reverting brings the old pages back.

## Summary
Moves React i18n, the connection indicator and React styling from the
Hilla section to the Flow Reference and the Styling section. This is the
eighth step of dissolving the Hilla section, after #6611, #6616, #6618,
#6620, #6621, #6622 and #6624.

Every statement is checked against `main` of vaadin/hilla and
vaadin/flow, and the behavior is checked in a scratch application on
25.4.0-alpha5 with Flow 25.4.0-alpha6, the first Flow release with
vaadin/flow#26714. This pull request targets `main` only, so it gets no
`target/v25.3` label.

## What changed
- **New page** `flow/i18n/react-views.adoc`, Localization in React
Views, rewritten from the Hilla i18n guide with compiled examples:
- loading translations with `i18n.configure()`, and how the initial
language is chosen;
- `translate()` and the `key` tag, the missing-translation marker, and
ICU parameters compared with Flow's `MessageFormat`;
- translating view titles and menu items with `translateDynamic()`,
including the `@Menu` titles of Flow views;
- changing the language, the file fallback, and that the React language
is separate from the Flow `UI` locale;
- development and production loading, and what a custom `I18NProvider`
must implement.
- **Loading and Connection Indicator**
(`flow/advanced/loading-indicator.adoc`) now also covers what the Hilla
connection indicator page covered:
- it covers Flow and React views, the loading bar stages, and the
connection status messages;
- configuration from Java, including `ReconnectDialogConfiguration`,
which no page documented, with a compiled example;
- configuration from TypeScript, with the `jar-resources` import that
replaces `@vaadin/common-frontend` after vaadin/flow#26714;
  - styling, with the element structure and states.
- **Stylesheets:** a new Stylesheets in React Views section: global
`@StyleSheet` styles, importing CSS files and CSS modules from a view,
and the type declarations these imports need.
- **Localization and Custom I18N Provider** link to the new page.
- **Removed pages, with redirects:**
  - `hilla/guides/i18n` → `flow/i18n/react-views`
- `hilla/reference/connection-indicator` →
`flow/advanced/loading-indicator` (the Lit redirect points there too)
  - `hilla/guides/styling` → `styling/stylesheets#react-views`
- `hilla/reference`, now empty → `flow/react-views` (the Lit redirect
points there too). This was planned for the last step.
  - The last partial in `articles/hilla/_shared` is removed.
- **Links:** the Routing page links to the new i18n page.
- **Guidelines:** the Hilla paragraph no longer mentions the `_shared`
partials, and the feature flag guideline no longer names the Hilla
Feature Flags section that #6624 removed.
- **Corrections** compared with the old pages, each checked in the
scratch application:
  - A missing translation shows as `!de: key`, not as the key.
- The old guide wrapped `i18n.configure()` in an effect. It returns a
`Promise`, and the page awaits it once in the layout.
  - The default first delay of the loading bar is 450 ms, not 300 ms.
- The old CSS examples didn't show anything with the default theme
turned off: the indicator's functional style, which is always present,
comes after the application's stylesheets and sets `opacity: 0` and
`pointer-events: none`. The examples now use
`vaadin-connection-indicator .v-loading-indicator` and set `opacity`.
- The `@vaadin/common-frontend` import no longer works after
vaadin/flow#26714.

## Test summary
- `npm run check-redirects` passes.
- `mvn spotless:apply` and `mvn compile` pass. `npx eslint
frontend/demo/fusion/i18n` passes.
- The two TypeScript examples type-check and run in the scratch
application, in English and German.
- Checked with the browser:
- translations, the ICU plural form, `{0}` placeholders, the missing-key
marker, on-demand keys and their warning;
- the cookie, the fallback from `de-DE` to `de` and the root file, and
the separate Flow locale;
- a custom provider without `getAllTranslations()` in development mode
(no translations; `initialized` stays `false`);
- the loading bar stages during a slow service call, the offline message
after a failed call and after the browser's `offline` event, and the
online message afterwards;
  - the reconnecting message of a Flow view with the server stopped;
- the TypeScript configuration, the Java configuration overriding only
the properties it sets, and the corrected curtain CSS;
- global styles, a plain CSS import that stays loaded after navigating
away, and a CSS module.
- Every cross reference in the new and changed lines resolves.
- Vale: no errors or warnings on the added and changed lines with the
pull request configuration.
- Asciidoctor renders the changed pages, with all includes and tags
resolved.
- I didn't run a full dspublisher build locally.

The TypeScript configuration snippet for the indicator is inline: the
docs build is on 25.4.0-alpha5, whose `Flow.js` still uses
`@vaadin/common-frontend`, so a compiled example importing the new
module wouldn't build yet.

Product bugs found during testing, reported separately:
- vaadin/flow#26970: `types.d.ts` doesn't declare CSS imports, so with
TypeScript 6 the imports in React views fail the type check. The
Stylesheets section documents the declarations to add.
- vaadin/hilla#7050: after `setLanguage()`, a key that
`translateDynamic()` loaded on demand stays empty. The page doesn't
describe this behavior.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants