React is a JavaScript library for building reactive user interfaces. Meteor provides seamless integration with React through the react-meteor-data package, allowing you to build modern, reactive applications.
Installation
Install React in your Meteor application:
meteor npm install --save react react-dom
For reactive data integration, add the react-meteor-data package:
meteor add react-meteor-data
Quick Start
Create a new React app with Meteor:
meteor create my-app --react
This creates a basic structure with React configured and ready to use.
Basic Setup
Client Entry Point
Render your React application in /client/main.jsx:
import React from 'react' ;
import { createRoot } from 'react-dom/client' ;
import { Meteor } from 'meteor/meteor' ;
import { App } from '/imports/ui/App' ;
Meteor . startup (() => {
const container = document . getElementById ( 'react-target' );
const root = createRoot ( container );
root . render ( < App /> );
});
Root Component
Create your root component in /imports/ui/App.jsx:
import React from 'react' ;
import { Hello } from './Hello.jsx' ;
import { Info } from './Info.jsx' ;
export const App = () => (
< div >
< h1 > Welcome to Meteor! </ h1 >
< Hello />
< Info />
</ div >
);
Reactive Data with useTracker
The useTracker hook integrates React components with Meteor’s reactive data system.
Basic Usage
import { useTracker } from 'meteor/react-meteor-data' ;
import { Meteor } from 'meteor/meteor' ;
function UserProfile () {
const currentUser = useTracker (() => Meteor . user (), []);
return < h1 > Hello { currentUser ?. username } </ h1 > ;
}
With Subscriptions
import { useTracker } from 'meteor/react-meteor-data' ;
import { TasksCollection } from '../api/tasks' ;
function TaskList ({ listId }) {
const { tasks , isLoading } = useTracker (() => {
const handle = Meteor . subscribe ( 'tasks' , listId );
return {
isLoading: ! handle . ready (),
tasks: TasksCollection . find ({ listId }). fetch ()
};
}, [ listId ]);
if ( isLoading ) {
return < div > Loading... </ div > ;
}
return (
< ul >
{ tasks . map ( task => (
< li key = { task . _id } > { task . title } </ li >
)) }
</ ul >
);
}
Modern Hooks API
useSubscribe
Convenient subscription management:
import { useFind , useSubscribe } from 'meteor/react-meteor-data' ;
import { LinksCollection } from '../api/links' ;
export const Info = () => {
const isLoading = useSubscribe ( 'links' );
const links = useFind (() => LinksCollection . find ());
if ( isLoading ()) {
return < div > Loading... </ div > ;
}
return (
< div >
< h2 > Learn Meteor! </ h2 >
< ul >
{ links . map ( link => (
< li key = { link . _id } >
< a href = { link . url } target = "_blank" > { link . title } </ a >
</ li >
)) }
</ ul >
</ div >
);
};
useFind
Optimized list rendering with controlled document references:
import { useFind , useSubscribe } from 'meteor/react-meteor-data' ;
import { memo } from 'react' ;
import { PostsCollection } from '../api/posts' ;
const PostItem = memo (({ post }) => (
< li > { post . title } </ li >
));
function PostsList ({ groupId }) {
useSubscribe ( 'posts' , groupId );
const posts = useFind (() => PostsCollection . find ({ groupId }), [ groupId ]);
return (
< ul >
{ posts . map ( post => (
< PostItem key = { post . _id } post = { post } />
)) }
</ ul >
);
}
useFind requires a cursor, not .fetch(). It uses Cursor.observe to efficiently update only changed documents.
withTracker HOC
For class components or legacy code:
import { withTracker } from 'meteor/react-meteor-data' ;
import { TasksCollection } from '../api/tasks' ;
function TaskList ({ currentUser , isLoading , tasks }) {
return (
< div >
< h1 > Hello { currentUser ?. username } </ h1 >
{ isLoading ? (
< div > Loading... </ div >
) : (
< ul >
{ tasks . map ( task => (
< li key = { task . _id } > { task . title } </ li >
)) }
</ ul >
) }
</ div >
);
}
export default withTracker (({ listId }) => {
const handle = Meteor . subscribe ( 'tasks' , listId );
return {
currentUser: Meteor . user (),
isLoading: ! handle . ready (),
tasks: TasksCollection . find ({ listId }). fetch ()
};
} )( TaskList ) ;
Suspense Support (Meteor 3.0+)
Use suspendable hooks with React Suspense:
import { useTracker , useSubscribe } from 'meteor/react-meteor-data/suspense' ;
import { TasksCollection } from '../api/tasks' ;
function Tasks () {
// Component will suspend until subscription is ready
useSubscribe ( 'tasks' );
const { username } = useTracker ( 'user' , () => Meteor . user ());
const tasks = useTracker (
'tasksByUser' ,
() => TasksCollection . find (
{ username },
{ sort: { createdAt: - 1 } }
). fetchAsync ()
);
return (
< ul >
{ tasks . map ( task => (
< li key = { task . _id } > { task . title } </ li >
)) }
</ ul >
);
}
Suspendable hooks require a unique key as the first argument to identify computations.
Routing with React Router
Meteor works seamlessly with React Router:
meteor npm install react-router-dom
import { BrowserRouter , Routes , Route } from 'react-router-dom' ;
import { Home } from './pages/Home' ;
import { TaskList } from './pages/TaskList' ;
export const App = () => (
< BrowserRouter >
< Routes >
< Route path = "/" element = { < Home /> } />
< Route path = "/tasks/:listId" element = { < TaskList /> } />
</ Routes >
</ BrowserRouter >
);
Integration Patterns
React in Blaze
Blaze in React
Use React components inside Blaze templates with react-template-helper: meteor add react-template-helper
In your Blaze template: < template name = "userDisplay" >
< div > Hello, {{username}} </ div >
< div > {{> React component=UserAvatar userId=_id}} </ div >
</ template >
Define the component helper: import { Template } from 'meteor/templating' ;
import UserAvatar from './UserAvatar.js' ;
Template . userDisplay . helpers ({
UserAvatar () {
return UserAvatar ;
}
});
Use Blaze templates in React components with gadicc:blaze-react-component: meteor add gadicc:blaze-react-component
import React from 'react' ;
import Blaze from 'meteor/gadicc:blaze-react-component' ;
const App = () => (
< div >
< Blaze template = "itemsList" items = { items } />
</ div >
);
Best Practices
The useTracker hook is preferred over withTracker for function components. Hooks provide better composition and are easier to test.
Optimize with dependency arrays
Provide dependency arrays to useTracker to retain computations and prevent unnecessary re-runs: const user = useTracker (() => Meteor . user (), []);
const tasks = useTracker (() => Tasks . find ({ listId }). fetch (), [ listId ]);
Use useFind for large lists
For rendering large lists from collections, useFind provides significant performance benefits by controlling object references.
Wrap list items with React.memo() to prevent unnecessary re-renders: const ListItem = memo (({ task }) => (
< li > { task . title } </ li >
));
Resources
React Tutorial Step-by-step tutorial for building React apps with Meteor
React Packages Source code and documentation for react-meteor-data
React Documentation Official React documentation and guides
React Router Client-side routing for React applications