Routes and Params

Nullstack has built-in routes, it would make no sense otherwise since web applications are expected to have hyperlinks.

Any tag can receive a route attribute, be it a component, inner component, or simple HTML tag.

import Nullstack from 'nullstack';
import Page from './Page';

class Application extends Nullstack {

  renderHome() {
    return (
      <section> Home </section>
    )
  }
 
  render({count}) {
    return (
      <main>
        <Home route="/" />
        <Page route="/page">
        <abbr route="/abbreviations"> Abbreviations </abbr>
      </main>
    )
  }

}

export default Application;

Links on Nullstack are simple a tags with the href value starting with "/".

<a href="/page/about"> About Page </a>

πŸ’‘ On the client side the click event will push history without reloading the page.

✨ You can still assign your own click event to the tag without losing the framework behavior.

Params

The params key is an object proxy injected into every client instance.

Each query string param is mapped to this object.

By default any key you request from this object will return a string.

If the value is undefined it will return an empty string.

If the value is true or false it will return a boolean instead.

πŸ±β€πŸ’» Bellow an exemple that visits "/books?expanded=true&page=2":

import Nullstack from 'nullstack';

class Books extends Nullstack {

  async initiate({params}) {
    if(params.expanded) {
      const page = parseInt(params.page) || 1;
      this.books = await this.getBooks({page});
    }
  }

}

export default Books;

Assigning to a params key will cause a redirect to the route with updated params.

When you assign to a param, the value will be converted to JSON before being set.

πŸ’‘ Redirects work in batches, so there is no performance loss in multiple assignments.

import Nullstack from 'nullstack';

class Paginator extends Nullstack {

  handleClick({params}) {
    params.filter = '';
    params.page = 1;
  }

}

export default Paginator;

Assigning an empty string to a param will remove it from the url.

Dynamic Segments

Part of the route can be an expression started with ":" followed by a param name.

This value will be matched against any string in the same directory position.

The value of the string in the URL will be assigned to the context params and functions below this point in the hierarchy will have access to the new key.

πŸ±β€πŸ’» Bellow an example that visits "/category/suspense?page=2":

import Nullstack from 'nullstack';

class Books extends Nullstack {

  async initiate({params}) {
    const page = parseInt(params.page) || 1;
    const category = params.slug;
    this.books = await this.getBooks({category, page});
  }

}

export default Books;
import Nullstack from 'nullstack';
import Books from './Books';

class Application extends Nullstack {

  render() {
    <main>
      <Books route="/category/:slug">
    </main>
  }

}

export default Application;

When a dynamic segment is changed, as for example moving from "/category/suspense" to "/category/comedy", the component will be terminated and a new instance will be created.

Changing a query param will not re-instantiate the component.

Children of the component will not be re-instantiated automatically, you can set the same route to the children or do it manually if you desire this behavior.

πŸ’‘ The behavior mentioned above solves many of the problems you have to normally deal with manually.

Wildcards

Wildcards are routes declared with "*" as the attribute value.

These routes will match anything if nothing above it matches the requested URL.

import Nullstack from 'nullstack';
import Home from './Home';

class Application extends Nullstack {

  render({count}) {
    return (
      <main>
        <Home route="/" />
        <div route="*"> Wildcard </abbr>
      </main>
    )
  }

}

Wildcards can be prefixed with a segment.

✨ this is especially useful for engines that can be mounted in your application.

import Nullstack from 'nullstack';
import Home from './Home';
import BlogEngine from './BlogEngine';

class Application extends Nullstack {

  render({count}) {
    return (
      <main>
        <Home route="/" />
        <BlogEngine route="/blog/*" />
      </main>
    )
  }

}

Router

The router key is an object proxy injected into every client instance.

The router has two keys:

  • url
  • path

The url key returns everything after the domain including the path and the query params as a string.

The path key returns only the path without query params.

πŸ’‘ Both keys above automatically remove the trailing slash for convenience.

Assigning to url or path will cause a redirect.

πŸ’‘ Under the hood a tags and params use the router.

import Nullstack from 'nullstack';

class Application extends Nullstack {

  prepare({router}) {
    if(router.path == '/') {
      router.path = '/dashboard';
    }
  }

}

Custom Events

Updating router.url or router.path will raise a custom event.

import Nullstack from 'nullstack';

class Analytics extends Nullstack {

  hydrate({router}) {
    window.addEventListener(router.event, () => {
      console.log(router.url);
    });
  }

}

export default Analytics;

Special anchors

Anchor tags accept some convenient special attributes besides the regular href.

You can set the params attribute with an object as the value.

The path will remain the same as the current router path, but the params will be replaced by the new params you specify.

<a params={{page: 1}}> First Page </a>

If you wish to just update some params and keep the others, you can use the javascript spread operator for that.

<a params={{...params, page: 1}}> First Page </a>

You can set the path attribute with a string starting with "/" and no query params.

The params will remain the same, but the path will be updated.

<a path="/category/suspense"> Suspense Books </a>

Both attributes above can be used at the same time.

<a path="/category/suspense" params={{...params, page: 1}}> Suspense Books </a>

Nested routes

The first route to be matched will be rendered.

The other elements with a route will not be rendered, however, elements on the same level without a route attribute will render normally.

The router will lookup for one route per dom depth level, this allows you to have nested routing behavior.

import Nullstack from 'nullstack';
import Home from './Home';

class Application extends Nullstack {

  renderPage() {
    return (
      <section>
        <div route="/page/about"> About Page </div>
        <div route="/page/contact"> Contact Page </div>
      </section>
    )
  }
 
  render({count}) {
    return (
      <main>
        <Home route="/" />
        <Page route="/page/:slug">
      </main>
    )
  }

}

export default Application;

Next step

βš” Learn about two-way bindings.