
Hello fellow developers!
I'm excited to share the latest updates to three essential Meteor.js libraries that I've been maintaining since 2015:ostrio:flow-router-extra
,ostrio:flow-router-title
, andostrio:flow-router-meta
.
These packages streamline tasks related to managing routing, document titles, and meta-tags within Meteor applications. With the recent release of Meteor v3 and Blaze v3, I've updated these libraries to ensure compatibility and to introduce compatibility with modern asynchronous patterns, making them powerful tools for building scalable web applications today for tomorrow.
About FlowRouter and its Evolution
When I first ventured intoMeteor.js development, I recognized a need for more flexible and feature-rich tools to handle routing and metadata management. The originalflow-router
was a solid foundation, but I saw opportunities to extend its capabilities, leading to the creation ofostrio:flow-router-extra
. This package superseded originalFlowRouter library as it wasn't updated for the past 9 years.ostrio:flow-router-extra
provides a more versatile routing solution, supporting multiple front-end frameworks including Blaze, Vue, React, Svelte, and beyond. Building upon this,ostrio:flow-router-title
andostrio:flow-router-meta
were developed to offer seamless management ofdocument.title
and meta-tags with support of reactive data sources.
What's New inostrio:flow-router-extra@3.12.0
The latest release, version 3.12.0, brings several noteworthy enhancements:
- Asynchronous Hooks:
action
,data
, andonNoData
hooks now support asynchronous functions, allowing for more efficient data fetching and rendering workflows - Improved Compatibility: The package has been updated to ensure seamless integration with
meteor@3
and other modern packages, enhancing overall stability and performance - Enhanced TypeScript Support: TypeScript definitions have been refined, providing better type checking and developer experience
For a detailed overview, you can refer to thev3.12.0
release notes.
What's unique about FlowRouter and how is it used?
Here are my recent use cases and some development tasks that were solved using FlowRouter packages:
- Hybrid Front-Ends: Imagine project with React dashboards, Vue marketing pages, and Blaze for the rest and legacy pages. Since Flow-Router-Extra is framework-agnostic it allowed my team to keep routing logic inside FlowRouter and use three different frameworks within single application and project
- E-Commerce Platform: Imagine a site with thousands of products. Using dynamic loading, we've cut loading times by loading product data and components only when a user visits a specific product page. The
data()
andwaitOn
hooks making this seamless, passing fetched data directly to templates. This also reducing bundle size and improving performance of the webapp - SEO Optimization: Paired with FlowRouterMeta and FlowRouterTitle, it ensures every route has tailored meta-tags and titles. For a blog platform we were recently working on, this boosted search rankings effortlessly
Integratingostrio:flow-router-extra
with Various Front-End Frameworks
One of the strengths ofostrio:flow-router-extra
is its framework-agnostic design, enabling integration with Blaze, Vue, React, Svelte, and more. Read further to see examples of how to use FlowRouter with different frameworks.
ostrio:flow-router-extra
supports "exact code splitting" via dynamic data and component loading — allowing to load related components and data on per-route basis excluding those files from "app's bundle", this is great solution for performance and flexibility.
Blaze Integration
Since Blaze engine is part of Meteor ecosystem — FlowRouter shipped with built-in rendering and templating engine for Blaze.this.render()
method is available inside route's hooks.
import{FlowRouter}from'meteor/ostrio:flow-router-extra';FlowRouter.route('/',{name:'home',action(){// render directly to <body>this.render('homeTemplate');}});FlowRouter.route('/',{name:'home',action(){// render into {{> yield}} placeholder inside layoutthis.render('layout','home');}});
Note:
kadira:blaze-layout
is outdated and wasn't updated in the past 10 years, it's advised to use built-inthis.render()
method to render Blaze templates.
To learn about all features related to Blaze-templating —read detailed "templating" tutorial.
Vue Integration
Createapp
usingnew Vue()
orcreateApp
, then import and render Vue Components insideaction()
hook
importVuefrom'vue';import{FlowRouter}from'meteor/ostrio:flow-router-extra';constapp=newVue({el:'#app',data:{currentComponent:null},render(h){// Render the current component, or a fallback if it's not setreturnthis.currentComponent?h(this.currentComponent):h('div','Loading...');}});FlowRouter.route('/',{name:'home',asyncaction(){// Dynamically import the HomeComponentconstHomeComponent=awaitimport('/imports/ui/HomeComponent.vue');// Update the reactive property with the imported component.app.currentComponent=HomeComponent.default||HomeComponent;}});
React Integration
Import and render React Components usingreact-dom
insideaction()
hook
importReactfrom'react';import{render}from'react-dom';import{FlowRouter}from'meteor/ostrio:flow-router-extra';FlowRouter.route('/',{name:'home',asyncaction(){const{default:HomeComponent}=awaitimport('/imports/ui/HomeComponent.jsx');render(<HomeComponent/>,document.getElementById('app'));}});
Same can get achieved usingmount
for better templating withApp
as layout
importReactfrom'react';import{FlowRouter}from'meteor/ostrio:flow-router-extra';import{mount}from'react-mounter';importAppfrom'/imports/ui/App.jsx';FlowRouter.route('/',{name:'home',asyncaction(){const{default:HomeComponent}=awaitimport('/imports/ui/HomeComponent.jsx');mount(App,{content:<HomeComponent/>});}});
Svelte Integration
Import and render Svelte Components insideaction()
hook
import{FlowRouter}from'meteor/ostrio:flow-router-extra';FlowRouter.route('/',{name:'home',asyncaction(){const{default:HomeComponent}=awaitimport('/imports/ui/HomeComponent.svelte');newHomeComponent({target:document.getElementById('app')});}});
Key features examples
Read further to see how newly updated API can be used in day-to-day tasks.
Fetch data from Meteor.methods
We all know that pub/sub should be used only when live data updates are "must have" for everything else we useMeteor.callAsync
on the Client andMeteor.methods
on the Server. Here's an example how to utilize it inFlowRouter
route anddata()
hook. Data returned from this hook is passed asthird argument to all other hooks within the same route:
FlowRouter.route('/post/:_id',{name:'post',asyncdata(params){constpost=awaitMeteor.callAsync('post.get',params._id);if(!post){return;}return{post};},action(params,queryParams,{post}){this.render('postTemplate',{post});},title(params,queryParams,{post}){returnpost?.title||'Post not found';},onNoData(){this.render('page404');}});
Dynamic Module Loading withwaitOn
Hook
ThewaitOn
hook inostrio:flow-router-extra
facilitates dynamic loading of front-end code on a per-route basis, implementing the "exact-code-splitting" paradigm. This approach ensures that only the necessary code for a specific route is loaded, optimizing performance. UsewhileWaiting
hook to render "Loading..." Component while data and modules are loading inwaitOn
hook. For example:
importLoadingComponentfrom'/imports/ui/LoadingComponent.jsx';constpublicRoutes=FlowRouter.group({title:"\"Meteor App Title\", // Default title for public routes"whileWaiting(){// Render Loading... spinner template between navigationrender(<LoadingComponent/>,document.getElementById('app'));}});publicRoutes.route('/about',{name:'about',waitOn(){returnimport('/imports/ui/AboutComponent.jsx');},asyncaction(){// import() will resolve instantly as it was cached after calling the same import inside `waitOn` hookconst{default:AboutComponent}=awaitimport('/imports/ui/AboutComponent.jsx');render(<AboutComponent/>,document.getElementById('app'));},});
In this example, theAboutComponent
is dynamically imported only when the/about
route is accessed, reducing the initial bundle size and improving load times for users.
Asynchronous Data Fetching withdata
Hook
Thedata
hook now supports asynchronous functions, allowing developers to fetch data before a route is rendered and pass it directly to the template or component. Value returned fromdata()
hook passed as third argument to all other hooks within the same route:
FlowRouter.route('/post/:_id',{name:'post',waitOn(params){returnMeteor.subscribe('post.get',params._id);},asyncdata(params){constpost=awaitPostsCollection.findOneAsync({_id:params._id});if(!post){return;}return{post};},action(params,queryParams,{post}){this.render('postTemplate',{post});},title(params,queryParams,{post}){returnpost?.title||'Post not found';},onNoData(){this.render('page404');}});
Here, thedata
hook asynchronously retrieves a post by its_id
and passes it to thepostTemplate
for rendering.
Use Cases and Problem Solving
Here are more examples how FlowRouter packages can improve solving day-to-day web development tasks
Managing Document Titles withostrio:flow-router-title
Keeping the document title in sync with the current route enhances user experience and SEO. Withostrio:flow-router-title
, you can define global title and titles per route:
import{FlowRouter}from'meteor/ostrio:flow-router-extra';import{FlowRouterTitle}from'meteor/ostrio:flow-router-title';FlowRouter.globals.push({title:'My App Title'});FlowRouter.route('/about',{name:'about',title:'About Us',action(){this.render('aboutTemplate');}});
Build breadcrumbs indocument.title
usingtitlePrefix
option ingroup
and nested Groups:
import{FlowRouter}from'meteor/ostrio:flow-router-extra';import{FlowRouterTitle}from'meteor/ostrio:flow-router-title';// My App > ProfileconstprofileRoutes=FlowRouter.group({title:'Profile'titlePrefix:'My App >'});profileRoutes.route('/profile',{action(){this.render('profile');}});// My App > Profile > SettingsconstsettingsRoutes=profileRoutes.group({title:'Settings'titlePrefix:'Profile >'});settingsRoutes.route('/profile/settings',{action(){this.render('profileSettings');}});
Learn more about managingdocument.title
inostrio:flow-router-title
package documentation
Dynamic Meta-Tags withostrio:flow-router-meta
Meta-tags play a crucial role in SEO and social media sharing. Withostrio:flow-router-meta
, you can set meta-tags dynamically based on the route:
import{FlowRouter}from'meteor/ostrio:flow-router-extra';import{FlowRouterMeta}from'meteor/ostrio:flow-router-meta';FlowRouter.route('/product/:id',{name:'product',asyncdata(params){constproduct=awaitProductsCollection.findOneAsync({_id:params.id});return{product};},meta(params,qs,{product}){return{'description':product.description,'og:image':product.imageUrl};},action(params,queryParams,{product}){this.render('productTemplate',{product});}});
In this example, meta-tags such as the description and Open Graph image are dynamically set based on the product data, improving SEO and the appearance of shared links. Learn about all features ofostrio:flow-router-meta
package in its documentation. To ensure all meta-tags and page's content properly rendered when visited by search engines — usepre-rendering service.
Flow-Router-Extra and its companion packages are must-haves forMeteor.js developers. Officially recommended by the Meteor team, they offer unmatched flexibility, performance, and compatibility. Whether you're building an SPA, optimizing for SEO, or juggling multiple frameworks, these tools have you covered. Dive into the docs, try the examples, and let me know how they work for you—happy coding!
Why Meteor.js
As we roll into 2025, Meteor.js remains a stellar choice for web development, and I'm here to tell you why from a developer's perspective. Its full-stack JavaScript platform continues to shine with its ability to deliver real-time, reactive applications out of the box — think chats, collaborative tools, or dashboards where data updates instantly across clients without breaking a sweat.
The latest Meteor 3.1 with Node.js 22 support, keep it cutting-edge, integrating seamlessly with modern front-end frameworks like React, Vue, Svelte, and our beloved Blaze
What makes it stand out is its simplicity: you write one language (JavaScript) for both client and server, slashing the cognitive load of juggling multiple tech stacks. Plus, features like the Distributed Data Protocol (DDP) and built-in reactivity mean you're not reinventing the wheel to sync data — something that's still a hassle in many other setups (I know and experienced it in the first hand). Add to that its zero-config development experience, TypeScript inference, and one-command build
Meteor.js is a tool that lets you focus on building features, not wrestling with boilerplate.
Meteor.js isn't just a framework — it's a full-blown development environment and a framework-agnostic build tool that's a no-brainer for both dev and production stages. The brightest minds at Meteor Software and the open-source community keep it thriving, pushing updates like the recent Express integration for RESTful APIs and maintaining a vibrant ecosystem of packages. It's not resting on its laurels from 2012; it's evolving with input from a dedicated Discord community and GitHub contributors.
Beyond Meteor itself, its DNA runs deep in famous open-source projects: Apollo GraphQL, born from the Meteor Development Group's pivot to a new backend layer, revolutionized data fetching, while Vulcan.js built a React/GraphQL stack atop Meteor's foundations. Even tools like InjectDetect trace their roots back to Meteor's real-time ethos. As a developer, I love that Meteor's build system handles transpilation, bundling, and hot module replacement without me touching a config file — whether I'm prototyping or shipping to production, it's a productivity booster that's hard to beat, even in 2025.
Further reading
Top comments(7)

I've been using Meteor for almost 10 years now, and I've seen how much the community has grown and improved thanks to contributors like you, Dmitriy. Your packages have been instrumental in making my development process smoother and more efficient. Your contributions are a valuable asset to the community. Thank you for all that you do!

- LocationWorld Wide Web
- WorkCTO at veliovgroup.com
- Joined
IMHO Meteor.js has one of the strongest communities, where the same contributors and maintainers dedicated to framework and its ecosystem for decades.
I’m glad to be part of it, and happy when my packages help other developers to solve daily tasks and businesses to achieve their goals

- LocationBremen, Germany
- EducationM.Sc.
- Pronounshe/him
- WorkScientific Employee at University of Bremen
- Joined
Wow you covered all the integrations thats full service 🔥

- LocationWorld Wide Web
- WorkCTO at veliovgroup.com
- Joined
Thank you Jan 🙏
Let me know if there’s any other library that can play well with FlowRouter

Another great article, man! Keep them coming.

- LocationWorld Wide Web
- WorkCTO at veliovgroup.com
- Joined
Thank you Gabs 🙏
Trying to pick the pace now 😅👨💻

- LocationWorld Wide Web
- WorkCTO at veliovgroup.com
- Joined
Do you have better examples? Or another great story about Meteor.js? Please share in the comments
For further actions, you may consider blocking this person and/orreporting abuse