RefreshLess

Layers JavaScript-based navigation on top of Drupal's server-rendered HTML to provide smooth, fast, SPA-like user experience while maintaining progressive enhancement.

refreshless
7 sites
66
drupal.org

Overview

RefreshLess transforms traditional Drupal server-rendered pages into a smooth, responsive single-page application (SPA)-like experience without sacrificing progressive enhancement. Built on the Hotwire Turbo library, it intercepts page navigation and form submissions, loading new content via AJAX and seamlessly updating the DOM while preserving loaded JavaScript and CSS.

Unlike full client-side SPAs, RefreshLess maintains complete fallback to traditional page loads if JavaScript fails, ensuring your site remains functional in adverse network conditions. This approach reuses Drupal's existing Twig templates, asset library system, caching, and security features, eliminating the need to re-implement a separate front-end.

Key capabilities include intelligent stylesheet and script management across navigations, client-side page caching for instant back/forward navigation, optional page transitions with CSS animations or the View Transitions API, lazy link preloading, and seamless integration with Drupal's AJAX system, dialogs, contextual links, and media library.

Features

  • JavaScript-based navigation that intercepts link clicks and form submissions, loading content via fetch API and updating the DOM without full page reloads
  • Progressive enhancement with automatic fallback to traditional page loads when JavaScript fails or is unavailable
  • Intelligent asset management that tracks loaded CSS and JavaScript libraries across navigations, only loading new assets as needed
  • Client-side page caching powered by Turbo for instant back/forward navigation and preview rendering while fetching fresh content
  • Optional page transitions with customizable CSS animations and support for the View Transitions API
  • Lazy link preloading that uses IntersectionObserver to preload links into cache as they scroll into view
  • Decorated messenger service that preserves flash messages across Turbo reload scenarios
  • Compatibility layers for Drupal core features including AJAX, dialogs, contextual links, media library, navigation module, and table select
  • Progress bar indicator showing page load status during navigation
  • Kill switch service to programmatically disable RefreshLess for specific requests
  • Cache contexts for conditional rendering based on RefreshLess state

Use Cases

Content-heavy websites requiring fast navigation

RefreshLess is ideal for content-rich Drupal sites where users frequently navigate between pages. By eliminating full page reloads, navigation feels instant while maintaining SEO benefits of server-rendered content. The client-side caching makes back/forward navigation instantaneous.

Progressive enhancement for mission-critical sites

For websites that must function reliably regardless of JavaScript availability (government sites, healthcare, e-commerce checkout), RefreshLess provides SPA-like UX while automatically falling back to traditional navigation if JavaScript fails. This ensures 100% uptime for core functionality.

Sites with expensive JavaScript initialization

If your site loads substantial JavaScript libraries or has complex initialization routines, RefreshLess eliminates the cost of re-initializing on every page load. CSS and JavaScript are additively loaded only when new assets are needed.

Admin interfaces with frequent navigation

Drupal administration interfaces benefit significantly from RefreshLess as editors frequently navigate between content listing, editing, and configuration pages. Combined with the Gin integration module, admin workflows become noticeably faster and more responsive.

Sites requiring page transitions and animations

Using the refreshless_transitions sub-module, sites can implement smooth page transitions with CSS animations or the modern View Transitions API, creating app-like experiences while maintaining traditional URL-based navigation.

Mobile-first websites

The refreshless_preloader sub-module is particularly valuable for mobile users where hover-based prefetching doesn't work. Links are preloaded as users scroll them into view, making subsequent navigation instant.

Tips

  • All RefreshLess JavaScript uses the defer attribute for optimal loading. Ensure your custom JavaScript also uses defer for compatibility.
  • Use the data-refreshless-temporary attribute on elements that should not be cached, similar to Turbo's data-turbo-temporary attribute.
  • When writing Drupal behaviors, always implement detach callbacks that fully restore elements to their original state. Use the trigger parameter to handle different detach scenarios.
  • Enable the refreshless_debug module during development to see all RefreshLess events logged to the browser console.
  • For lazy link preloading, add data-refreshless-lazy-preload to container elements rather than individual links. Opt out specific links with data-refreshless-lazy-preload="false".
  • The kill switch service (refreshless.kill_switch) can be used to disable RefreshLess for specific requests programmatically - useful for pages with unique requirements.
  • RefreshLess automatically handles theme changes by triggering a full reload. Design your site with this in mind if users frequently switch between themes.
  • Custom CSS properties in the page transition overlay allow theming without JavaScript changes. Override --refreshless-page-transition-* properties in your theme.
  • The refreshless:before-render event allows delaying render with Promises - useful for implementing custom exit animations before content changes.
  • Listen to refreshless:drupal-settings-update to react to drupalSettings changes, which provides previous, new, and merged settings objects.

Technical Details

Troubleshooting 8
RefreshLess navigation not working - pages reload fully

Check that all required Drupal core patches are applied. Run 'composer install' to ensure patches are applied. Verify that JavaScript console shows no errors. Ensure the refreshless_turbo sub-module is enabled.

BigPipe compatibility error during installation

RefreshLess is currently not compatible with the BigPipe module. Uninstall BigPipe before installing RefreshLess. See issue #3519784 for progress on BigPipe compatibility.

Flash messages disappearing after navigation

This is handled automatically by the decorated messenger service. If messages still disappear, ensure the refreshless_turbo module is enabled and the messenger service decoration is active.

JavaScript behaviors not attaching after navigation

Ensure your behaviors have proper detach callbacks that fully clean up changes. Use once() and once.remove() from @drupal/once library. Don't store DOM element references outside behavior callbacks.

CSS specificity issues after navigation

RefreshLess maintains stylesheet order to preserve CSS specificity. If issues persist, listen to the refreshless:stylesheets-merged event and trigger refreshless:sort-stylesheets if needed.

Page reloads unexpectedly instead of using RefreshLess

Check browser console for the reload reason. Common causes: theme change (e.g., switching between frontend and admin theme), CSS/JS aggregation state change, session permission hash change. These are expected behaviors to ensure consistency.

Prefetching not working on certain links

Some links may be excluded from prefetching via drupalSettings.refreshless.prefetchExcludePaths or data-turbo-prefetch="false" attribute. Check if the link's path is in the exclusion list.

Page transitions not appearing

Ensure the refreshless_transitions sub-module is enabled. The transition overlay relies on CSS opacity transitions - do not remove the opacity property. Check that data-refreshless-page-transition-state attribute is being set on the html element.

Security Notes 5
  • RefreshLess maintains Drupal's CSRF protection through theme tokens stored in the page state cookie.
  • The page state cookie uses secure and sameSite:strict attributes by default for enhanced security.
  • Session permission hash changes trigger a full page reload to ensure users always see content appropriate for their current permissions.
  • The debug logging module (refreshless_debug) is explicitly designed to not expose sensitive data and is safe for production use.
  • All RefreshLess requests include an identifying header (x-refreshless-turbo) that can be used for server-side request identification and security policies.