Fresh 2.3.0 is a broad feature release with a lot of user-facing surface area. The biggest additions are first-class WebSocket support, View Transitions for client-side navigation, and a set of app/runtime options that make Fresh easier to run behind proxies and in more deployment setups.
What's new
- Adds first-class WebSocket support through
ctx.upgrade()andapp.ws(). You can now upgrade a request in either bare mode, where you get{ socket, response }, or managed mode withopen,message,close, anderrorhandlers. The newapp.ws(path, handlers, options?)helper registers a GET route that upgrades automatically. (#3774) - Adds View Transitions API support for client-side navigation. It is opt-in with
f-view-transition, works with links, popstate, and form submissions, and falls back cleanly when the browser does not support the API. (#3708) - Adds
trustProxytoFreshConfigsoctx.urlcan respectX-Forwarded-ProtoandX-Forwarded-Hostheaders behind reverse proxies. This is off by default. (#3757) - Adds an IP filter middleware with allow lists, deny lists, and CIDR subnet matching. Deny rules win over allow rules. (#3035)
- Adds nonce support for inline
<script>and<style>tags in CSP. Enable it withapp.use(csp({ useNonce: true })), and Fresh will inject per-request nonces during SSR. (#3709) - Adds support for passing Temporal objects to islands. Serialization now covers all eight Temporal types, including
Instant,PlainDate,ZonedDateTime, andDuration. (#3701) - Adds
_freshIndicatorsupport for partial form submits, so the loading signal now covers form submissions as well as anchor clicks. (#3753) - Adds support for multiple
staticDirentries. Fresh now acceptsstring | string[]and searches directories in order, with the first match winning. (#3759) - Adds support for
deno create @fresh/initfor project initialization on Deno 2.7+, while keeping the olderdeno run -Ar jsr:@fresh/initflow available before 2.3.0. (#3706, #3746) - Adds
HttpErrorfor clients. (#3080) - Improves docs and the landing page substantially, including a full documentation audit and a refreshed homepage for Fresh 2.3. (#3712, #3775)
Breaking changes
FreshRequest.reqis no longerreadonly, so code that depended on that type modifier will need to account for the field being mutable now. (#2751)
Fixes
- Fixes partial redirects so
fresh-partialis preserved throughctx.redirect(), and falls back to a full navigation when a redirect target has no partials. (#3715, #3716) - Fixes active link detection to consider query parameters and leave an existing
aria-currentvalue alone. (#3755) - Fixes
<Head>so it works client-side. (#3252) - Fixes trailing-slash mismatches that could make static routes 404. (#3721)
- Fixes middleware matching for filesystem routes with optional parameters. (#3726)
- Fixes programmatic layouts so they apply to index routes too. (#3725)
- Fixes
node_modulesasset requests in dev mode, which unblocked fonts and other assets referenced from npm package CSS. (#3728) - Fixes the dev server when
basePathis configured so Vite virtual URLs are passed through correctly. (#3730) - Fixes CSS Modules in
_app,_layout, and_error, and across routes in dev. (#3764) - Fixes static file handling for spaces, commas,
.well-known, and other encoded paths. (#3659, #3770, #3676) - Fixes the updater, update-tool output, and route error messages so they are more accurate and easier to act on. (#3630, #3754, #3718)
- Fixes Vite asset caching so hashed JS and CSS assets are treated as immutable. (#3761)
- Fixes WebSocket-related hydration and client-side behavior around invalid HTML nesting by warning instead of crashing. (#3762)
Other notable changes
- Fresh now actually omits the client entry script when neither islands nor partials are used, matching the “no JS by default” promise. (#3696)
- Middleware chains are pre-compiled at build time instead of being constructed per request. (#3104)
- The Vite plugin got a broad set of compatibility fixes, including better Deno module resolution and improved CommonJS-to-ESM transforms for npm packages. (#3734, #3697)
deno createsupport was later rolled back in docs and init help for pre-2.3.0 flows, keeping the older init command as the default guidance until the new path is available. (#3746)- The docs site, homepage, and showcase were cleaned up and reorganized as part of the release. (#3712, #3703, #3775)
Contributors
@bartlomieju, @fry69, @marvinhagemeister, @Octo8080X, @Ionaru, @yukitaka, @ghalle, @CertainLach, @cuipeiyu, @dahlia, @Ott??