Routing
Overview
One of the most powerful features of Mango is the ability to develop a Multi-Page Application (MPA), a Single-Page Application (SPA) or an application that's a mixture of both. You can even create API endpoints as a part of your application instead of having to manage a separate API server.
File Structure
Routing in Mango is controlled by the structure of your routes
directory. The routes
directory is where you define your application's routes. Files defining content exposed to the public have special names starting with +
. They can be classified into the following types:
+page.jsx
- A page that can be accessed by a user. It is a regular Mango component representing the root of the page. This type simulates the behavior of MPA since the page is only shown when the user navigates to its exact path.+pages.jsx
- The same as+page.jsx
but it is shown when the user navigates to any path that starts with the path of the file. This type simulates the behavior of SPA since it is shown when the user navigates to any path that starts with the path of the file.@mango-js/router
package is used to retrieve the extra part of the path.+get.js
- A file that defines a GET endpoint. It is a Node.js module that exports a default function very similar to the function exported by.ssr.js
files. API endpoints are explained in more detail in the API section.+post.js
- A file that defines a POST endpoint. It works the same way as+get.js
but it is used for POST requests.+put.js
- A file that defines a PUT endpoint. It works the same way as+get.js
but it is used for PUT requests.+patch.js
- A file that defines a PATCH endpoint. It works the same way as+get.js
but it is used for PATCH requests.+delete.js
- A file that defines a DELETE endpoint. It works the same way as+get.js
but it is used for DELETE requests.
Assuming routes
directory is the root of your routes, path to the directory containing the file defining a route is the public path of that route. For example, if you have a file routes/users/+get.js
then the public path of that route is /users
. URL parameters can be defined by wrapping the name of the parameter in square brackets. For example, if you have a file routes/users/[id]/+get.js
then the public path of that route is /users/:id
. The value of the parameter can be retrieved by using @mango-js/router
package.
Here is an example of one possible tree structure of the routes
directory:
routes ├── api │ ├── animals │ │ ├── [animalId] │ │ │ ├── +get.js │ │ │ ├── +post.js │ │ │ ├── +patch.js │ │ │ └── +delete.js │ │ └── +get.js ├── animals │ ├── +pages.jsx │ └── styles.module.scss ├── blog │ ├── [postId] │ │ ├── +page.jsx │ │ └── styles.module.scss │ ├── +page.jsx │ └── styles.module.scss ├── about │ ├── +page.jsx │ └── styles.module.scss ├── contact | ├── +post.js │ ├── +page.jsx │ └── styles.module.scss
Current Route
Current route can be defined by a unique set of the following attributes:
params
- Route parameters determined by the route pattern.query
- Query parameters determined by the query string after the "?".pattern
- Route pattern that matched the current URL.hash
- Hash of the current URL determined by the string after the "#".
Those properties can be retrieved anywhere in the client-side application by using @mango-js/router
package. For SSR functions, they are passed as an object as discussed in the dynamic content section.
// App.jsx import { $routeParams, $routeQuery, $routePath, $routeHash, $routePattern, navigate, } from "@mango-js/router"; export default function App() { return ( <div> <h1>Current Route</h1> <p>Params:</p> <ul> {Object.keys($routeParams).map((key) => ( <li> {key}: {JSON.stringify($routeParams[key])} </li> ))} </ul> <p>Query:</p> <ul> {Object.keys($routeQuery).map((key) => ( <li> {key}: {JSON.stringify($routeQuery[key])} </li> ))} </ul> <p>Path: {$routePath}</p> <p>Hash: {$routeHash}</p> <p>Pattern: {$routePattern}</p> <button onClick={() => navigate("/users/123?name=John&age=25#hash")}> Navigate </button> </div> ); }
The interface of functions and variables exported by @mango-js/router
package is as follows:
/** * Route parameters determined by the route pattern. * * @example * // Assuming the current URL is "/foo/bar/baz" * // And the route pattern is "/foo/:bar/:baz" * $routeParams.bar // "bar" * $routeParams.baz // "baz" * $routeParams.qux // undefined * $routeParams["*"] // undefined * @example * // Assuming the current URL is "/foo/bar/baz/qux/quux" * // And the route pattern is "/foo/:bar/:baz/*" * $routeParams.bar // "bar" * $routeParams.baz // "baz" * $routeParams["*"] // "/qux/quux" */ export var $routeParams: { [x: string]: string; }; /** * Query parameters determined by the query string after the "?". * * @example * // Assuming the current URL is "/?foo=bar&baz=qux" * $routeQuery.foo // "bar" * $routeQuery.baz // "qux" */ export var $routeQuery: { [x: string]: string; }; /** * Pathname of the current URL determined by the string before the "?" and "#". * * @example * // Assuming the current URL is "/foo/bar?baz=qux#quux" * $routePath // "/foo/bar" */ export var $routePath: string; /** * Hash of the current URL determined by the string after the "#". * * @example * // Assuming the current URL is "/?foo=bar#baz" * $routeHash // "baz" */ export var $routeHash: string; /** * Route pattern that matched the current URL. * * @example * // Assuming the current URL is "/foo/bar/baz" * // And the route pattern is "/foo/:bar/:baz" * $routePattern // "/foo/[bar]/[baz]" */ export var $routePattern: string; /** * Navigates to a new path. * * @param {string | number} nextPath - Path to navigate to or a number to go back/forward in history. * @param {boolean} shouldReplace - Whether to replace the current history entry or not. */ export function navigate(nextPath: string | number, shouldReplace: boolean): void;