` that contains the children passed to the component, but in a real life scenario it could be as complex as needed.
One way to wrap the layout is to define an anonymous component in the routes map:
```javascript
// example 1
m.route(document.body, "/", {
"/": {
view: function() {
return m(Layout, m(Home))
},
},
"/form": {
view: function() {
return m(Layout, m(Form))
},
}
})
```
However, note that because the top level component is an anonymous component, jumping from the `/` route to the `/form` route (or vice-versa) will tear down the anonymous component and recreate the DOM from scratch. If the Layout component had [lifecycle methods](lifecycle-methods.md) defined, the `oninit` and `oncreate` hooks would fire on every route change. Depending on the application, this may or may not be desirable.
If you would prefer to have the Layout component be diffed and maintained intact rather than recreated from scratch, you should instead use a RouteResolver as the root object:
```javascript
// example 2
m.route(document.body, "/", {
"/": {
render: function() {
return m(Layout, m(Home))
},
},
"/form": {
render: function() {
return m(Layout, m(Form))
},
}
})
```
Note that in this case, if the Layout component has `oninit` and `oncreate` lifecycle methods, they would only fire on the first route change (assuming all routes use the same layout).
To clarify the difference between the two examples, example 1 is equivalent to this code:
```javascript
// functionally equivalent to example 1
var Anon1 = {
view: function() {
return m(Layout, m(Home))
},
}
var Anon2 = {
view: function() {
return m(Layout, m(Form))
},
}
m.route(document.body, "/", {
"/": {
render: function() {
return m(Anon1)
}
},
"/form": {
render: function() {
return m(Anon2)
}
},
})
```
Since `Anon1` and `Anon2` are different components, their subtrees (including `Layout`) are recreated from scratch. This is also what happens when components are used directly without a RouteResolver.
In example 2, since `Layout` is the top-level component in both routes, the DOM for the `Layout` component is diffed (i.e. left intact if it has no changes), and only the change from `Home` to `Form` triggers a recreation of that subsection of the DOM.
---
#### Authentication
The RouteResolver's `onmatch` hook can be used to run logic before the top level component in a route is initialized. The example below shows how to implement a login wall that prevents users from seeing the `/secret` page unless they login.
```javascript
var isLoggedIn = false
var Login = {
view: function() {
return m("form", [
m("button[type=button]", {
onclick: function() {
isLoggedIn = true
m.route.set("/secret")
}
}, "Login")
])
}
}
m.route(document.body, "/secret", {
"/secret": {
onmatch: function() {
if (!isLoggedIn) m.route.set("/login")
else return Home
}
},
"/login": Login
})
```
When the application loads, `onmatch` is called and since `isLoggedIn` is false, the application redirects to `/login`. Once the user pressed the login button, `isLoggedIn` would be set to true, and the application would redirect to `/secret`. The `onmatch` hook would run once again, and since `isLoggedIn` is true this time, the application would render the `Home` component.
For the sake of simplicity, in the example above, the user's logged in status is kept in a global variable, and that flag is merely toggled when the user clicks the login button. In a real life application, a user would obviously have to supply proper login credentials, and clicking the login button would trigger a request to a server to authenticate the user:
```javascript
var Auth = {
username: "",
password: "",
setUsername: function(value) {
Auth.username = value
},
setPassword: function(value) {
Auth.password = value
},
login: function() {
m.request({
url: "/api/v1/auth",
params: {username: Auth.username, password: Auth.password}
}).then(function(data) {
localStorage.setItem("auth-token", data.token)
m.route.set("/secret")
})
}
}
var Login = {
view: function() {
return m("form", [
m("input[type=text]", {
oninput: function (e) { Auth.setUsername(e.target.value) },
value: Auth.username
}),
m("input[type=password]", {
oninput: function (e) { Auth.setPassword(e.target.value) },
value: Auth.password
}),
m("button[type=button]", {onclick: Auth.login}, "Login")
])
}
}
m.route(document.body, "/secret", {
"/secret": {
onmatch: function() {
if (!localStorage.getItem("auth-token")) m.route.set("/login")
else return Home
}
},
"/login": Login
})
```
---
#### Preloading data
Typically, a component can load data upon initialization. Loading data this way renders the component twice. The first render pass occurs upon routing, and the second fires after the request completes. Take care to note that `loadUsers()` returns a Promise, but any Promise returned by `oninit` is currently ignored. The second render pass comes from the [`background` option for `m.request`](request.md).
```javascript
var state = {
users: [],
loadUsers: function() {
return m.request("/api/v1/users").then(function(users) {
state.users = users
})
}
}
m.route(document.body, "/user/list", {
"/user/list": {
oninit: state.loadUsers,
view: function() {
return state.users.length > 0 ? state.users.map(function(user) {
return m("div", user.id)
}) : "loading"
}
},
})
```
In the example above, on the first render, the UI displays `"loading"` since `state.users` is an empty array before the request completes. Then, once data is available, the UI redraws and a list of user ids is shown.
RouteResolvers can be used as a mechanism to preload data before rendering a component in order to avoid UI flickering and thus bypassing the need for a loading indicator:
```javascript
var state = {
users: [],
loadUsers: function() {
return m.request("/api/v1/users").then(function(users) {
state.users = users
})
}
}
m.route(document.body, "/user/list", {
"/user/list": {
onmatch: state.loadUsers,
render: function() {
return state.users.map(function(user) {
return m("div", user.id)
})
}
},
})
```
Above, `render` only runs after the request completes, making the ternary operator redundant.
---
#### Code splitting
In a large application, it may be desirable to download the code for each route on demand, rather than upfront. Dividing the codebase this way is known as code splitting or lazy loading. In Mithril, this can be accomplished by returning a promise from the `onmatch` hook:
At its most basic form, one could do the following:
```javascript
// Home.js
module.export = {
view: function() {
return [
m(Menu),
m("h1", "Home")
]
}
}
```
```javascript
// index.js
function load(file) {
return m.request({
method: "GET",
url: file,
extract: function(xhr) {
return new Function("var module = {};" + xhr.responseText + ";return module.exports;")
}
})
}
m.route(document.body, "/", {
"/": {
onmatch: function() {
return load("Home.js")
},
},
})
```
However, realistically, in order for that to work on a production scale, it would be necessary to bundle all of the dependencies for the `Home.js` module into the file that is ultimately served by the server.
Fortunately, there are a number of tools that facilitate the task of bundling modules for lazy loading. Here's an example using [native dynamic `import(...)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import), supported by many bundlers:
```javascript
m.route(document.body, "/", {
"/": {
onmatch: function() {
return import('./Home.js')
},
},
})
```
---
### Typed routes
In certain advanced routing cases, you may want to constrain a value further than just the path itself, only matching something like a numeric ID. You can do that pretty easily by returning `m.route.SKIP` from a route.
```javascript
m.route(document.body, "/", {
"/view/:id": {
onmatch: function(args) {
if (!/^\d+$/.test(args.id)) return m.route.SKIP
return ItemView
},
},
"/view/:name": UserView,
})
```
---
### Hidden routes
In rare circumstances, you may want to hide certain routes for some users, but not all. For instance, a user might be prohibited from viewing a particular user, and instead of showing a permission error, you'd rather pretend it doesn't exist and redirect to a 404 view instead. In this case, you can use `m.route.SKIP` to just pretend the route doesn't exist.
```javascript
m.route(document.body, "/", {
"/user/:id": {
onmatch: function(args) {
return Model.checkViewable(args.id).then(function(viewable) {
return viewable ? UserView : m.route.SKIP
})
},
},
"/:404...": PageNotFound,
})
```
---
### Route cancellation / blocking
RouteResolver `onmatch` can prevent route resolution by returning a promise that never resolves. This can be used to detect attempted redundant route resolutions and cancel them:
```javascript
m.route(document.body, "/", {
"/": {
onmatch: function(args, requestedPath) {
if (m.route.get() === requestedPath)
return new Promise(function() {})
},
},
})
```
---
### Third-party integration
In certain situations, you may find yourself needing to interoperate with another framework like React. Here's how you do it:
- Define all your routes using `m.route` as normal, but make sure you only use it *once*. Multiple route points are not supported.
- When you need to remove routing subscriptions, use `m.mount(root, null)`, using the same root you used `m.route(root, ...)` on. `m.route` uses `m.mount` internally to hook everything up, so it's not magic.
Here's an example with React:
```jsx
class Child extends React.Component {
constructor(props) {
super(props)
this.root = React.createRef()
}
componentDidMount() {
m.route(this.root, "/", {
// ...
})
}
componentDidUnmount() {
m.mount(this.root, null)
}
render() {
return
}
}
```
And here's the rough equivalent with Vue:
```html
```
```javascript
Vue.component("my-child", {
template: `
`,
mounted: function() {
m.route(this.$refs.root, "/", {
// ...
})
},
destroyed: function() {
m.mount(this.$refs.root, null)
},
})
```