Docs as single file
May 9, 2026 · View on GitHub
Quick Start
Install
# Remove original FlowRouter
meteor remove kadira:flow-router
# Install FR-Extra
meteor add ostrio:flow-router-extra
Note: This package is meant to replace original FlowRouter, kadira:flow-router should be removed to avoid interference and unexpected behavior.
ES6 Import
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
Create your first route
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
// Create index route
FlowRouter.route('/', {
name: 'index',
action() {
// Do something here
// After route is followed
this.render('templateName');
}
});
// Create 404 route (catch-all)
FlowRouter.route('*', {
action() {
// Show 404 error page
this.render('notFound');
}
});
Force template re-rendering
Introduced in v3.7.1
By default if same template is rendered when user navigates to a different route, including parameters or query-string change/update rendering engine will smoothly only update template's data. In case if you wish to force full template rendering executing all hooks and callbacks use { conf: { forceReRender: true } }, like:
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
import '/imports/client/layout/layout.js';
FlowRouter.route('/item/:_id', {
name: 'item',
conf: {
// without this option template won't be re-rendered
// upon navigation between different "item" routes
// e.g. when navigating from `/item/1` to `/item/2`
forceReRender: true
},
waitOn() {
return import('/imports/client/item/item.js');
},
action(params) {
this.render('layout', 'item', params);
}
});
Create a route with parameters
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
// Going to: /article/article_id/article-slug
FlowRouter.route('/article/:_id/:slug', {
name: 'article',
action(params) {
// All passed parameters is available as Object:
console.log(params);
// { _id: 'article_id', slug: 'article-slug' }
// Pass params to Template's context
this.render('article', params);
},
waitOn(params) {
return Meteor.subscribe('article', params._id);
}
});
Create a route with query string
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
// Going to: /article/article_id?comment=123
FlowRouter.route('/article/:_id', {
name: 'article',
action(params, queryParams) {
// All passed parameters and query string
// are available as Objects:
console.log(params);
// { _id: 'article_id' }
console.log(queryParams);
// { comment: '123' }
// Pass params and query string to Template's context
this.render('article', Object.assign({}, params, queryParams));
}
});
Note: if you're using any package which requires original FR namespace, throws an error, you can solve it with next code:
// in /lib/ directory
Package['kadira:flow-router'] = Package['ostrio:flow-router-extra'];
API
current method
FlowRouter.current();
- Returns {Object}
Get the current state of the router. This API is not reactive.
If you need to watch the changes in the path simply use FlowRouter.watchPathChange().
Example
// route def: /apps/:appId
// url: /apps/this-is-my-app?show=yes&color=red
const current = FlowRouter.current();
console.log(current);
// prints following object
// {
// path: "/apps/this-is-my-app?show=yes&color=red",
// params: {appId: "this-is-my-app"},
// queryParams: {show: "yes", color: "red"}
// route: {pathDef: "/apps/:appId", name: "name-of-the-route"}
// }
getParam method
FlowRouter.getParam(paramName);
paramName{String}- Returns {String}
Reactive function which you can use to get a parameter from the URL.
Example
// route def: /apps/:appId
// url: /apps/this-is-my-app
const appId = FlowRouter.getParam('appId');
console.log(appId); // prints "this-is-my-app"
getQueryParam method
FlowRouter.getQueryParam(queryKey);
queryKey{String}- Returns {String}
Reactive function which you can use to get a value from the query string.
// route def: /apps/:appId
// url: /apps/this-is-my-app?show=yes&color=red
const color = FlowRouter.getQueryParam('color');
console.log(color); // prints "red"
getRouteName method
FlowRouter.getRouteName();
- Returns {String}
Use to get the name of the route reactively.
Example
Tracker.autorun(function () {
const routeName = FlowRouter.getRouteName();
console.log('Current route name is: ', routeName);
});
go method
.go(path, params, queryParams)
path{String} - Path or Route's nameparams{Object} - Serialized route parameters,{ _id: 'str' }queryParams{Object} - Query params object,{ key: 'val' }- Returns {true}
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
FlowRouter.route('/blog', { name: 'blog' /* ... */ });
FlowRouter.route('/blog/:_id', { name: 'blogPost' /* ... */ });
FlowRouter.go('/blog'); // <-- by path - /blog/
FlowRouter.go('blog'); // <-- by Route's name - /blog/
FlowRouter.go('blogPost', { _id: 'post_id' }); // /blog/post_id
FlowRouter.go('blogPost', { _id: 'post_id' }, { commentId: '123' }); // /blog/post_id?commentId=123
If only the hash changes for the current path and query, FlowRouter leaves route logic alone and lets the browser handle it like normal anchor navigation. The route action does not re-run. Use the browser hashchange event for tab switches, scrolling, or other fragment-specific behavior.
window.addEventListener('hashchange', () => {
const tab = window.location.hash.slice(1);
if (tab) {
document.getElementById(tab)?.scrollIntoView();
}
});
FlowRouter.go('/profile#security'); // same /profile route, browser hash behavior
group method
Use group routes for better route organization.
.group(options)
options{Object} - [Optional]options.name{String} - [Optional] Route's nameoptions.prefix{String} - [Optional] Route prefixoptions[prop-name]{Any} - [Optional] Any property which will be available inside route calloptions[hook-name]{Function} - [Optional] See all available hooks- Returns {Group}
const adminRoutes = FlowRouter.group({
prefix: '/admin',
name: 'admin',
triggersEnter: [(context, redirect) => {
console.log('running group triggers');
}]
});
// handling /admin/ route
adminRoutes.route('/', {
name: 'adminIndex',
action() { /* ... */ }
});
// handling /admin/posts
adminRoutes.route('/posts', {
name: 'adminPosts',
action() { /* ... */ }
});
Nested Group
const adminRoutes = FlowRouter.group({
prefix: '/admin',
name: 'admin'
});
const superAdminRoutes = adminRoutes.group({
prefix: '/super',
name: 'superadmin'
});
// handling /admin/super/post
superAdminRoutes.route('/post', {
action() { /* ... */ }
});
Get group name
FlowRouter.current().route.group.name
This can be useful for determining if the current route is in a specific group (e.g. admin, public, loggedIn) without needing to use prefixes if you don't want to. If it's a nested group, you can get the parent group's name with:
FlowRouter.current().route.group.parent.name
initialize method
FlowRouter.initialize();
- Returns {void}
By default, FlowRouter initializes the routing process in a Meteor.startup() callback. This works for most of the applications. But, some applications have custom initializations and FlowRouter needs to initialize after that.
So, that's where FlowRouter.wait() comes to save you. You need to call it directly inside your JavaScript file. After that, whenever your app is ready call FlowRouter.initialize().
Example
FlowRouter.wait();
WhenEverYourAppIsReady(() => {
FlowRouter.initialize();
});
onRouteRegister method
FlowRouter.onRouteRegister(callback);
callback{Function}- Returns {void}
This API is specially designed for add-on developers. They can listen for any registered route and add custom functionality to FlowRouter. This works on both server and client alike.
FlowRouter.onRouteRegister((route) => {
// do anything with the route object
console.log(route);
});
path method
FlowRouter.path(path, params, queryParams);
path{String} - Path or Route's nameparams{Object} - Serialized route parameters,{ _id: 'str' }queryParams{Object} - Query params object,{ key: 'val' }- Returns {String} - URI
const pathDef = '/blog/:cat/:id';
const params = { cat: 'met eor', id: 'abc' };
const queryParams = {show: 'y+e=s', color: 'black'};
const path = FlowRouter.path(pathDef, params, queryParams);
console.log(path); // --> "/blog/met%20eor/abc?show=y%2Be%3Ds&color=black"
If there are no params or queryParams, it will simply return the path as it is.
pathRegExp option
// Use dashes as separators so `/:id-:slug/` isn't translated to `id-:slug` but to `:id`-`:slug`
FlowRouter.pathRegExp = /(:[\w\(\)\\\+\*\.\?\[\]]+)+/g;
pathRegExp{RegExp}- Default -
/(:[\w\(\)\\\+\*\.\?\[\]\-]+)+/g
Use to change the URI RegEx parser used for params, for more info see #25.
decodeQueryParamsOnce option
The current behavior of FlowRouter.getQueryParam("...") and FlowRouter.current().queryParams is to double-decode query params, but this can cause issues when, for example, you want to pass a URL with its own query parameters as a URI component, such as in an OAuth flow or a redirect after login.
To solve this, you can set this option to true to tell FlowRouter to only decode query params once.
// Allows us to pass things like encoded URLs as query params (default = false)
FlowRouter.decodeQueryParamsOnce = true;
Example
Given the URL in the address bar:
http://localhost:3000/signin?after=%2Foauth%2Fauthorize%3Fclient_id%3D123%26redirect_uri%3Dhttps%253A%252F%252Fothersite.com%252F
If decodeQueryParamsOnce is not set or set to false ❌ ...
FlowRouter.getQueryParam("after");
// returns: "/oauth/authorize?client_id=123"
FlowRouter.current().queryParams;
// returns: { after: "/oauth/authorize?client_id=123", redirect_uri: "https://othersite.com/" }
If decodeQueryParamsOnce is set to true ✔️ ...
FlowRouter.getQueryParam("after");
// returns: "/oauth/authorize?client_id=123&redirect_uri=https%3A%2F%2Fothersite.com%2F"
FlowRouter.current().queryParams;
// returns: { after: "/oauth/authorize?client_id=123&redirect_uri=https%3A%2F%2Fothersite.com%2F" }
The former is no longer recommended, but to maintain compatibility with legacy apps, false is the default value for this flag. Enabling this flag manually with true is recommended for all new apps. For more info, see #78.
refresh method
FlowRouter.refresh('layout', 'template');
layout{String} - [required] Name of the layout templatetemplate{String} - [required] Name of the intermediate template, simple<template>Loading...</template>might be a good option
FlowRouter.refresh() will force all route's rules and hooks to re-run, including subscriptions, waitOn(s) and template render.
Useful in cases where template logic depends from route's hooks, example:
{{#if currentUser}}
{{> yield}}
{{else}}
{{> loginForm}}
{{/if}}
in example above "yielded" template may loose data context after user login action, although user login will cause yield template to render - data and waitOn hooks will not fetch new data.
Login example
Meteor.loginWithPassword({
username: 'some@email.com'
}, 'password', error => {
if (error) {
/* show error */
} else {
/* If login form has its own `/login` route, redirect to root: */
if (FlowRouter._current.route.name === 'login') {
FlowRouter.go('/');
} else {
FlowRouter.refresh('_layout', '_loading');
}
}
});
Logout example
Meteor.logout((error) => {
if (error) {
console.error(error);
} else {
FlowRouter.refresh('_layout', '_loading');
}
});
reload method
FlowRouter.reload();
- Returns {void}
FlowRouter routes are idempotent. That means, even if you call FlowRouter.go() to the same URL multiple times, it only activates in the first run. This is also true for directly clicking on paths.
So, if you really need to reload the route, this is the method you want.
render method
this.render() method is available only inside hooks.
Note
this.render() method is available only if application has templating and blaze, or blaze-html-templates packages installed
With Layout
this.render(layout, template [, data, callback])
layout{String|Blaze.Template} - Blaze.Template instance or a name of layout template (which hasyield)template{String|Blaze.Template} - Blaze.Template instance or a name of template (which will be rendered into yield)data{Object} - [Optional] Object of data context to use in template. Will be passed to bothlayoutandtemplatecallback{Function} - [Optional] Callback triggered after template is rendered and placed into DOM. This callback has no context
Without Layout
this.render(template [, data, callback])
template{String|Blaze.Template} - Blaze.Template instance or a name of template (which will be rendered intobodyelement, or element defined inFlowRouter.Renderer.rootElement)data{Object} - [Optional] Object of data context to use in templatecallback{Function} - [Optional] Callback triggered after template is rendered and placed into DOM. This callback has no context
Features:
- Made with animation performance in mind, all DOM interactions are wrapped into
requestAnimationFrame - In-memory rendering (a.k.a. off-screen rendering, virtual DOM), disabled by default, can be activated with
FlowRouter.Renderer.inMemoryRendering = true;
Settings (Experimental!):
- Settings below is experimental, targeted to reduce on-screen DOM layout reflow, speed up rendering on slower devices and Phones in first place, by moving DOM computation to off-screen (a.k.a. In-Memory DOM, Virtual DOM)
FlowRouter.Renderer.rootElement{Function} - Function which returns root DOM element where layout will be rendered, default:document.bodyFlowRouter.Renderer.inMemoryRendering{Boolean} - Enable/Disable in-memory rendering, default:falseFlowRouter.Renderer.getMemoryElement{Function} - Function which returns default in-memory element, default:document.createElement('div'). Usedocument.createDocumentFragment()to avoid extra parent elements- The default
document.createElement('div')will cause extra wrappingdivelement document.createDocumentFragment()won't cause extra wrappingdivelement but may lead to exceptions in Blaze engine, depends from your app implementation
- The default
route method
.route(path, options)
path{String} - Path with placeholdersoptions{Object}options.name{String} - Route's nameoptions[prop-name]{Any} - [Optional] Any property which will be available inside route calloptions[hook-name]{Function} - [Optional] See all available hooks- Returns {Route}
import { FlowRouter } from 'meteor/ostrio:flow-router-extra';
FlowRouter.route('/blog/:cat/:id', {
name: 'blogPostRoute'
})
const params = {cat: "meteor", id: "abc"};
const queryParams = {show: "yes", color: "black"};
const path = FlowRouter.path("blogPostRoute", params, queryParams);
console.log(path); // prints "/blog/meteor/abc?show=yes&color=black"
Catch-all route
// Create 404 route (catch-all)
FlowRouter.route('*', {
action() {
// Show 404 error page
}
});
setParams method
FlowRouter.setParams(params);
params{Object} - Serialized route parameters,{ _id: 'str' }- Returns {true}
Change the current Route's params with the new values and re-route to the new path.
// route def: /apps/:appId
// url: /apps/this-is-my-app?show=yes&color=red
FlowRouter.setParams({appId: 'new-id'});
// Then the user will be redirected to the following path
// /apps/new-id?show=yes&color=red
setQueryParams method
FlowRouter.setQueryParams(queryParams);
queryParams{Object} - Query params object,{ key: 'val' }- Returns {true}
Unset parameter
To remove a query param set it to null:
FlowRouter.setQueryParams({ paramToRemove: null });
Global Triggers
FlowRouter.triggers.enter([cb1, cb2]);
FlowRouter.triggers.exit([cb1, cb2]);
// filtering
FlowRouter.triggers.enter([trackRouteEntry], {only: ["home"]});
FlowRouter.triggers.exit([trackRouteExit], {except: ["home"]});
To filter routes use only or except keywords.
You can't use both only and except at once.
url method
FlowRouter.url(path, params, queryParams);
path{String} - Path or Route's nameparams{Object} - Serialized route parameters,{ _id: 'str' }queryParams{Object} - Query params object,{ key: 'val' }- Returns {String} - Absolute URL using
Meteor.absoluteUrl
wait method
FlowRouter.wait();
- Returns {void}
By default, FlowRouter initializes the routing process in a Meteor.startup() callback. This works for most of the applications. But, some applications have custom initializations and FlowRouter needs to initialize after that.
So, that's where FlowRouter.wait() comes to save you. You need to call it directly inside your JavaScript file. After that, whenever your app is ready call FlowRouter.initialize().
Example
FlowRouter.wait();
WhenEverYourAppIsReady(() => {
FlowRouter.initialize();
});
watchPathChange method
FlowRouter.watchPathChange();
- Returns {void}
Reactively watch the changes in the path. If you need to simply get the params or queryParams use methods like FlowRouter.getQueryParam().
Tracker.autorun(() => {
FlowRouter.watchPathChange();
const currentContext = FlowRouter.current();
// do something with the current context
});
withReplaceState method
FlowRouter.withReplaceState(callback);
callback{Function}- Returns {void}
Normally, all the route changes made via APIs like FlowRouter.go and FlowRouter.setParams() add a URL item to the browser history. For example, run the following code:
FlowRouter.setParams({id: 'the-id-1'});
FlowRouter.setParams({id: 'the-id-2'});
FlowRouter.setParams({id: 'the-id-3'});
Now you can hit the back button of your browser two times. This is normal behavior since users may click the back button and expect to see the previous state of the app.
But sometimes, this is not something you want. You don't need to pollute the browser history. Then, you can use the following syntax.
FlowRouter.withReplaceState(() => {
FlowRouter.setParams({id: 'the-id-1'});
FlowRouter.setParams({id: 'the-id-2'});
FlowRouter.setParams({id: 'the-id-3'});
});
Hooks
action hook
action(params, queryParams, data)
params{Object} - Serialized route parameters,/route/:_id => { _id: 'str' }queryParams{Object} - Query params object,/route/?key=val => { key: 'val' }data{Mix} - Value returned from.data()hook- Return: {void}
.action() hook is triggered right after page is navigated to route, or after (exact order, if any of those is defined):
data hook
data(params, queryParams)
params{Object} - Serialized route parameters,/route/:_id => { _id: 'str' }queryParams{Object} - Query params object,/route/?key=val => { key: 'val' }- Return: {Mongo.Cursor|Object|[Object]|false|null|void}
.data() is triggered right after all resources in .waitOn() and .waitOnResources() hooks are ready.
FlowRouter.route('/post/:_id', {
name: 'post',
waitOn(params) {
return Meteor.subscribe('post', params._id);
},
async data(params, queryParams) {
return await PostsCollection.findOneAsync({_id: params._id});
}
});
Passing data into a Template
FlowRouter.route('/post/:_id', {
name: 'post',
action(params, queryParams, post) {
this.render('_layout', 'post', { post });
},
waitOn(params) {
return Meteor.subscribe('post', params._id);
},
async data(params, queryParams) {
return await PostsCollection.findOneAsync({_id: params._id});
}
});
<!-- in a template -->
<template name="post">
<h1>{{post.title}}</h1>
<p>{{post.text}}</p>
</template>
Data in other hooks
Returned value from data hook, will be passed into all other hooks as third argument and to triggersEnter hooks as fourth argument
FlowRouter.route('/post/:_id', {
name: 'post',
async data(params) {
return await PostsCollection.findOneAsync({_id: params._id});
},
triggersEnter: [(context, redirect, stop, data) => {
console.log(data);
}]
});
endWaiting hook
endWaiting() - Called with no arguments
- Return: {void}
.endWaiting() hook is triggered right after all resources in .waitOn() and .waitOnResources() hooks are ready.
onNoData hook
onNoData(params, queryParams)
params{Object} - Serialized route parameters,/route/:_id => { _id: 'str' }queryParams{Object} - Query params object,/route/?key=val => { key: 'val' }- Return: {void}
.onNoData() hook is triggered instead of .action() in case when .data() hook returns "falsy" value. Run any JavaScript code inside .onNoData() hook, for example render 404 template or redirect user somewhere else.
FlowRouter.route('/post/:_id', {
name: 'post',
async data(params) {
return await PostsCollection.findOneAsync({_id: params._id});
},
async onNoData(params, queryParams){
await import('/imports/client/page-404.js');
this.render('_layout', '_404');
}
});
triggersEnter
triggersEnter is option (not actually a hook), it accepts array of Functions, each function will be called with next arguments:
context{Route} - Output ofFlowRouter.current()redirect{Function} - Use to redirect to another route, same asFlowRouter.go()methodstop{Function} - Use to abort current route executiondata{Mix} - Value returned from.data()hook- Return: {void}
Scroll to top:
const scrollToTop = () => {
(window.scroll || window.scrollTo || function (){})(0, 0);
};
FlowRouter.route('/', {
name: 'index',
triggersEnter: [scrollToTop]
});
// Apply to every route:
FlowRouter.triggers.enter([scrollToTop]);
Logging:
FlowRouter.route('/', {
name: 'index',
triggersEnter: [() => {
console.log('triggersEnter');
}]
});
Redirect:
FlowRouter.route('/', {
name: 'index',
triggersEnter: [(context, redirect) => {
redirect('/other/route');
}]
});
Global
FlowRouter.triggers.enter([cb1, cb2]);
triggersExit hooks
triggersExit is option (not actually a hook), it accepts array of Functions, each function will be called with one argument:
context{Route} - Output ofFlowRouter.current()- Return: {void}
const trackRouteEntry = (context) => {
// context is the output of `FlowRouter.current()`
console.log("visit-to-home", context.queryParams);
};
const trackRouteClose = (context) => {
console.log("move-from-home", context.queryParams);
};
FlowRouter.route('/home', {
// calls just before the action
triggersEnter: [trackRouteEntry],
action() {
// do something you like
},
// calls when when we decide to move to another route
// but calls before the next route started
triggersExit: [trackRouteClose]
});
Global
FlowRouter.triggers.exit([cb1, cb2]);
waitOn hook
waitOn(params, queryParams, ready)
params{Object} - Serialized route parameters,/route/:_id => { _id: 'str' }queryParams{Object} - Query params object,/route/?key=val => { key: 'val' }ready{Function} - Call when computation is ready using Tracker- Return: {Promise|[Promise]|Subscription|[Subscription]|Tracker|[Tracker]}
.waitOn() hook is triggered before .action() hook, allowing to load necessary data before rendering a template.
maxWaitFor (route and router)
- Per route:
FlowRouter.route(path, { maxWaitFor, waitOn, action, … })— max time in milliseconds for resolvingwaitOnpromises (includingasync waitOn) and for waiting until every returned subscription-like handle’sready()is true (polled every 24ms). - Router default:
FlowRouter.maxWaitFordefaults to120000(same as package exportMAX_WAIT_FOR_MS). Set viaFlowRouter.initialize({ maxWaitFor })or assignFlowRouter.maxWaitFor = …. Routes without an explicitmaxWaitForuseFlowRouter.maxWaitForat the timewaitOnruns. - If
maxWaitForelapses while promises or subscriptions are still pending,waitOnends and the route still runstriggersEnter/action(timeout is logged). Navigation away abortswaitOnand skipsactionfor the route being left.
Subscriptions
FlowRouter.route('/post/:_id', {
name: 'post',
waitOn(params) {
return [Meteor.subscribe('post', params._id), Meteor.subscribe('suggestedPosts', params._id)];
}
});
Tracker
Use reactive data sources inside waitOn hook. To make waitOn rerun on reactive data changes, wrap it to Tracker.autorun and return Tracker Computation object or an Array of Tracker Computation objects. Note: the third argument of waitOn is ready callback.
FlowRouter.route('/posts', {
name: 'post',
waitOn(params, queryParams, ready) {
return Tracker.autorun(() => {
ready(() => {
return Meteor.subscribe('posts', search.get(), page.get());
});
});
}
});
Array of Trackers
FlowRouter.route('/posts', {
name: 'post',
waitOn(params, queryParams, ready) {
const tracks = [];
tracks.push(Tracker.autorun(() => {
ready(() => {
return Meteor.subscribe('posts', search.get(), page.get());
});
}));
tracks.push(Tracker.autorun(() => {
ready(() => {
return Meteor.subscribe('comments', postId.get());
});
}));
return tracks;
}
});
Promises
FlowRouter.route('/posts', {
name: 'posts',
waitOn() {
return new Promise((resolve, reject) => {
loadPosts((err) => {
(err) ? reject() : resolve();
});
});
}
});
Array of Promises
FlowRouter.route('/posts', {
name: 'posts',
waitOn() {
return [new Promise({/*..*/}), new Promise({/*..*/}), new Promise({/*..*/})];
}
});
Meteor method via Promise
Deprecated, since v3.12.0 Meteor.callAsync can get called inside async data() hook to retrieve data from a method
FlowRouter.route('/posts', {
name: 'posts',
conf: {
posts: false
},
action(params, queryParams, data) {
this.render('layout', 'posts', data);
},
waitOn() {
return new Promise((resolve, reject) => {
Meteor.call('posts.get', (error, posts) => {
if (error) {
reject();
} else {
// Use `conf` as shared object to
// pass it from `data()` hook to
// `action()` hook`
this.conf.posts = posts;
resolve();
}
});
});
},
data() {
return this.conf.posts;
}
});
Dynamic import
FlowRouter.route('/posts', {
name: 'posts',
waitOn() {
return import('/imports/client/posts.js');
}
});
Array of dynamic import(s)
FlowRouter.route('/posts', {
name: 'posts',
waitOn() {
return [
import('/imports/client/posts.js'),
import('/imports/client/sidebar.js'),
import('/imports/client/footer.js')
];
}
});
Dynamic import and Subscription
FlowRouter.route('/posts', {
name: 'posts',
waitOn() {
return [import('/imports/client/posts.js'), Meteor.subscribe('Posts')];
}
});
waitOnResources hook
waitOnResources(params, queryParams)
params{Object} - Serialized route parameters,/route/:_id => { _id: 'str' }queryParams{Object} - Query params object,/route/?key=val => { key: 'val' }- Return: {Object}
{ images: ['url'], other: ['url'] }
.waitOnResources() hook is triggered before .action() hook, allowing to load necessary files, images, fonts before rendering a template.
Preload images
FlowRouter.route('/images', {
name: 'images',
waitOnResources() {
return {
images:[
'/imgs/1.png',
'/imgs/2.png',
'/imgs/3.png'
]
};
},
});
Global
Useful to preload background images and other globally used resources
FlowRouter.globals.push({
waitOnResources() {
return {
images: [
'/imgs/background/jpg',
'/imgs/icon-sprite.png',
'/img/logo.png'
]
};
}
});
Preload Resources
This method will work only for cacheble resources, if URLs returns non-cacheble resources (dynamic resources) it will be useless.
Why Images and Other resources is separated? What the difference? - Images can be prefetched via Image() constructor, all other resources will use XMLHttpRequest to cache resources. Thats why important to make sure requested URLs returns cacheble response.
FlowRouter.route('/', {
name: 'index',
waitOnResources() {
return {
other:[
'/fonts/OpenSans-Regular.eot',
'/fonts/OpenSans-Regular.svg',
'/fonts/OpenSans-Regular.ttf',
'/fonts/OpenSans-Regular.woff',
'/fonts/OpenSans-Regular.woff2'
]
};
}
});
Global
Useful to prefetch Fonts and other globally used resources
FlowRouter.globals.push({
waitOnResources() {
return {
other:[
'/fonts/OpenSans-Regular.eot',
'/fonts/OpenSans-Regular.svg',
'/fonts/OpenSans-Regular.ttf',
'/fonts/OpenSans-Regular.woff',
'/fonts/OpenSans-Regular.woff2'
]
};
}
});
whileWaiting hook
whileWaiting(params, queryParams)
params{Object} - Serialized route parameters,/route/:_id => { _id: 'str' }queryParams{Object} - Query params object,/route/?key=val => { key: 'val' }- Return: {void}
.whileWaiting() hook is triggered before .waitOn() hook, allowing to display/render text or animation saying Loading....
FlowRouter.route('/post/:_id', {
name: 'post',
whileWaiting() {
this.render('loading');
},
waitOn(params) {
return Meteor.subscribe('post', params._id);
}
});
Template Helpers
Note
Template helpers are available only if application has templating and blaze, or blaze-html-templates packages installed
currentRouteName Template Helper
Returns the name of the current route
<div class={{currentRouteName}}>
...
</div>
currentRouteOption Template Helper
This adds support to get options from flow router
FlowRouter.route('name', {
name: 'routeName',
action() {
this.render('layoutTemplate', 'main');
},
coolOption: "coolOptionValue"
});
<div class={{currentRouteOption 'coolOption'}}>
...
</div>
isActivePath Template Helper
Template helper to check if the supplied path matches the currently active route's path.
Returns either a configurable String, which defaults to 'active', or false.
<li class="{{isActivePath '/home'}}">...</li>
<li class="{{isActivePath path='/home'}}">...</li>
<li class="{{isActivePath regex='home|dashboard'}}">...</li>
{{#if isActivePath '/home'}}
<span>Show only if '/home' is the current route's path</span>
{{/if}}
{{#if isActivePath regex='^\\/products'}}
<span>Show only if current route's path begins with '/products'</span>
{{/if}}
<li class="{{isActivePath class='is-selected' path='/home'}}">...</li>
<li class="{{isActivePath '/home' class='is-selected'}}">...</li>
isActiveRoute Template Helper
Template helper to check if the supplied route name matches the currently active route's name.
Returns either a configurable String, which defaults to 'active', or false.
<li class="{{isActiveRoute 'home'}}">...</li>
<li class="{{isActiveRoute name='home'}}">...</li>
<li class="{{isActiveRoute regex='home|dashboard'}}">...</li>
{{#if isActiveRoute 'home'}}
<span>Show only if 'home' is the current route's name</span>
{{/if}}
{{#if isActiveRoute regex='^products'}}
<span>Show only if the current route's name begins with 'products'</span>
{{/if}}
<li class="{{isActiveRoute class='is-selected' name='home'}}">...</li>
<li class="{{isActiveRoute 'home' class='is-selected'}}">...</li>
isNotActivePath Template Helper
Template helper to check if the supplied path doesn't match the currently active route's path.
Returns either a configurable String, which defaults to 'disabled', or false.
<li class="{{isNotActivePath '/home'}}">...</li>
<li class="{{isNotActivePath path='/home'}}">...</li>
<li class="{{isNotActivePath regex='home|dashboard'}}">...</li>
{{#if isNotActivePath '/home'}}
<span>Show only if '/home' isn't the current route's path</span>
{{/if}}
{{#if isNotActivePath regex='^\\/products'}}
<span>Show only if current route's path doesn't begin with '/products'</span>
{{/if}}
<li class="{{isNotActivePath class='is-disabled' path='/home'}}">...</li>
<li class="{{isNotActivePath '/home' class='is-disabled'}}">...</li>
isNotActiveRoute Template Helper
Template helper to check if the supplied route name doesn't match the currently active route's name.
Returns either a configurable String, which defaults to 'disabled', or false.
<li class="{{isNotActiveRoute 'home'}}">...</li>
<li class="{{isNotActiveRoute name='home'}}">...</li>
<li class="{{isNotActiveRoute regex='home|dashboard'}}">...</li>
{{#if isNotActiveRoute 'home'}}
<span>Show only if 'home' isn't the current route's name</span>
{{/if}}
{{#if isNotActiveRoute regex='^products'}}
<span>
Show only if the current route's name doesn't begin with 'products'
</span>
{{/if}}
<li class="{{isNotActiveRoute class='is-disabled' name='home'}}">...</li>
<li class="{{isNotActiveRoute 'home' class='is-disabled'}}">...</li>
Arguments
The following can be used by as arguments in isNotActivePath, isNotActiveRoute, isActivePath and isActiveRoute helpers.
- Data context, Optional.
StringorObjectwithname,pathorregex name{String} - Only available forisActiveRouteandisNotActiveRoutepath{String} - Only available forisActivePathandisNotActivePathregex{String|RegExp}
param Template Helper
Returns the value for a URL parameter
<div>ID of this post is <em>{{param 'id'}}</em></div>
pathFor Template Helper
Used to build a path to your route. First parameter can be either the path definition or name you assigned to the route. After that you can pass the params needed to construct the path. Query parameters can be passed with the query parameter. Hash is supported via hash parameter.
<a href="{{pathFor '/post/:id' id=_id}}">Link to post</a>
<a href="{{pathFor 'postRouteName' id=_id}}">Link to post</a>
<a href="{{pathFor '/post/:id/comments/:cid' id=_id cid=comment._id}}">Link to comment in post</a>
<a href="{{pathFor '/post/:id/comments/:cid' id=_id hash=comment._id}}">Jump to comment</a>
<a href="{{pathFor '/post/:id/comments/:cid' id=_id cid=comment._id query='back=yes&more=true'}}">Link to comment in post with query params</a>
Same-route hash links use browser fragment behavior. FlowRouter does not run route hooks or actions when only the hash changes; handle fragment-specific UI with hashchange.
window.addEventListener('hashchange', () => {
const commentId = window.location.hash.slice(1);
document.getElementById(commentId)?.scrollIntoView();
});
queryParam Template Helper
Returns the value for a query parameter
<input placeholder="Search" value="{{queryParam 'query'}}">
urlFor Template Helper
Used to build an absolute URL to your route. First parameter can be either the path definition or name you assigned to the route. After that you can pass the params needed to construct the URL. Query parameters can be passed with the query parameter. Hash is supported via hash parameter.
<a href="{{urlFor '/post/:id' id=_id}}">Link to post</a>
<a href="{{urlFor 'postRouteName' id=_id}}">Link to post</a>
<a href="{{urlFor '/post/:id/comments/:cid' id=_id cid=comment._id}}">Link to comment in post</a>
<a href="{{urlFor '/post/:id/comments/:cid' id=_id hash=comment._id}}">Jump to comment</a>
<a href="{{urlFor '/post/:id/comments/:cid' id=_id cid=comment._id query='back=yes&more=true'}}">Link to comment in post with query params</a>