DEV Community

Cover image for Meteor.js: FlowRouter packages update
Dmitriy A.
Dmitriy A.

Posted on

Meteor.js: FlowRouter packages update

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, and ostrio: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.

flow-router-extra-artwork

About FlowRouter and its Evolution

When I first ventured into Meteor.js development, I recognized a need for more flexible and feature-rich tools to handle routing and metadata management. The original flow-router was a solid foundation, but I saw opportunities to extend its capabilities, leading to the creation of ostrio:flow-router-extra. This package superseded original FlowRouter 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 and ostrio:flow-router-meta were developed to offer seamless management of document.title and meta-tags with support of reactive data sources.

What's New in ostrio:flow-router-extra@3.12.0

The latest release, version 3.12.0, brings several noteworthy enhancements:

  • Asynchronous Hooks: action, data, and onNoData 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 the v3.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() and waitOn 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

Integrating ostrio:flow-router-extra with Various Front-End Frameworks

One of the strengths of ostrio: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.

flow-router-extra-sitemap-artwork

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 layout
    this.render('layout', 'home');
  }
});
Enter fullscreen mode Exit fullscreen mode

Note: kadira:blaze-layout is outdated and wasn't updated in the past 10 years, it's advised to use built-in this.render() method to render Blaze templates.

To learn about all features related to Blaze-templating β€” read detailed "templating" tutorial.

Vue Integration

Create app using new Vue() or createApp, then import and render Vue Components inside action() hook

import Vue from 'vue';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';

const app = new Vue({
  el: '#app',
  data: {
    currentComponent: null
  },
  render(h) {
    // Render the current component, or a fallback if it's not set
    return this.currentComponent ? h(this.currentComponent) : h('div', 'Loading...');
  }
});

FlowRouter.route('/', {
  name: 'home',
  async action() {
    // Dynamically import the HomeComponent
    const HomeComponent = await import('/imports/ui/HomeComponent.vue');
    // Update the reactive property with the imported component.
    app.currentComponent = HomeComponent.default || HomeComponent;
  }
});
Enter fullscreen mode Exit fullscreen mode

React Integration

Import and render React Components using react-dom inside action() hook

import React from 'react';
import { render } from 'react-dom';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';

FlowRouter.route('/', {
  name: 'home',
  async action() {
    const { default: HomeComponent } = await import('/imports/ui/HomeComponent.jsx');
    render(<HomeComponent />, document.getElementById('app'));
  }
});
Enter fullscreen mode Exit fullscreen mode

Same can get achieved using mount for better templating with App as layout

import React from 'react';
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { mount } from 'react-mounter';
import App from '/imports/ui/App.jsx';

FlowRouter.route('/', {
  name: 'home',
  async action() {
    const { default: HomeComponent } = await import('/imports/ui/HomeComponent.jsx');
    mount(App, {
      content: <HomeComponent />
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

Svelte Integration

Import and render Svelte Components inside action() hook

import { FlowRouter } from 'meteor/ostrio:flow-router-extra';

FlowRouter.route('/', {
  name: 'home',
  async action() {
    const { default: HomeComponent } = await import('/imports/ui/HomeComponent.svelte');
    new HomeComponent({
      target: document.getElementById('app')
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

Key features examples

Read further to see how newly updated API can be used in day-to-day tasks.

Fetch data from Meteor.Method

We all know that pub/sub should be used only when live data updates are "must have" for everything else we use Meteor.callAsync on the Client and Meteor.Methods on the Server. Here's and example how to utilize it in FlowRouter route and data() hook. Data returned from this hook is passed as third argument to all other hooks within the same route:

FlowRouter.route('/post/:_id', {
  name: 'post',
  async data(params) {
    const post = await Meteor.callAsync('post.get', params._id);
    if (!post) {
      return;
    }
    return { post };
  },
  action(params, queryParams, { post }) {
    this.render('postTemplate', { post });
  },
  title(params, queryParams, { post }) {
    return post?.title || 'Post not found';
  },
  onNoData() {
    this.render('page404');
  }
});
Enter fullscreen mode Exit fullscreen mode

Dynamic Module Loading with waitOn Hook

The waitOn hook in ostrio: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. Use whileWaiting hook to render "Loading..." Component while data and modules are loading in waitOn hook. For example:

import LoadingComponent from '/imports/ui/LoadingComponent.jsx';

const publicRoutes = FlowRouter.group({
  title: "'Meteor App Title', // Default title for public routes"
  whileWaiting() {
    // Render Loading... spinner template between navigation
    render(<LoadingComponent />, document.getElementById('app'));
  }
});

publicRoutes.route('/about', {
  name: 'about',
  waitOn() {
    return import('/imports/ui/AboutComponent.jsx');
  },
  async action() {
    // import() will resolve instantly as it was cached after calling the same import inside `waitOn` hook
    const { default: AboutComponent } = await import('/imports/ui/AboutComponent.jsx');
    render(<AboutComponent />, document.getElementById('app'));
  },
});
Enter fullscreen mode Exit fullscreen mode

In this example, the AboutComponent is dynamically imported only when the /about route is accessed, reducing the initial bundle size and improving load times for users.

Asynchronous Data Fetching with data Hook

The data 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 from data() hook passed as third argument to all other hooks within the same route:

FlowRouter.route('/post/:_id', {
  name: 'post',
  waitOn(params) {
    return Meteor.subscribe('post.get', params._id);
  },
  async data(params) {
    const post = await PostsCollection.findOneAsync({ _id: params._id });
    if (!post) {
      return;
    }
    return { post };
  },
  action(params, queryParams, { post }) {
    this.render('postTemplate', { post });
  },
  title(params, queryParams, { post }) {
    return post?.title || 'Post not found';
  },
  onNoData() {
    this.render('page404');
  }
});
Enter fullscreen mode Exit fullscreen mode

Here, the data hook asynchronously retrieves a post by its _id and passes it to the postTemplate 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 with ostrio:flow-router-title

Keeping the document title in sync with the current route enhances user experience and SEO. With ostrio: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');
 }
});
Enter fullscreen mode Exit fullscreen mode

Build breadcrumbs in document.title using titlePrefix option in group and nested Groups:

import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import { FlowRouterTitle } from 'meteor/ostrio:flow-router-title';

// My App > Profile
const profileRoutes = FlowRouter.group({
  title: 'Profile'
  titlePrefix: 'My App > '
});

profileRoutes.route('/profile', {
  action() {
    this.render('profile');
  }
});

// My App > Profile > Settings
const settingsRoutes = profileRoutes.group({
  title: 'Settings'
  titlePrefix: 'Profile > '
});

settingsRoutes.route('/profile/settings', {
  action() {
    this.render('profileSettings');
  }
});
Enter fullscreen mode Exit fullscreen mode

Learn more about managing document.title in ostrio:flow-router-title package documentation

Dynamic Meta-Tags with ostrio:flow-router-meta

Meta-tags play a crucial role in SEO and social media sharing. With ostrio: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',
 async data(params) {
   const product = await ProductsCollection.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 });
 }
});
Enter fullscreen mode Exit fullscreen mode

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 of ostrio:flow-router-meta package in its documentation. To ensure all meta-tags and page's content properly rendered when visited by search engines β€” use pre-rendering service.

Flow-Router-Extra and its companion packages are must-haves for Meteor.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)

Collapse
 
distalx profile image
D

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!

Collapse
 
smart_egg profile image
Dmitriy A.

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

Collapse
 
jankapunkt profile image
Jan KΓΌster πŸ”₯

Wow you covered all the integrations thats full service πŸ”₯

Collapse
 
smart_egg profile image
Dmitriy A.

Thank you Jan πŸ™
Let me know if there’s any other library that can play well with FlowRouter

Collapse
 
ogabsferreira profile image
Gabs Ferreira

Another great article, man! Keep them coming.

Collapse
 
smart_egg profile image
Dmitriy A.

Thank you Gabs πŸ™
Trying to pick the pace now πŸ˜…πŸ‘¨β€πŸ’»

Collapse
 
smart_egg profile image
Dmitriy A.

Do you have better examples? Or another great story about Meteor.js? Please share in the comments