Rocket Logo Rocket Docs Themes Tools Blog

Components

Components in Rocket are "just" the web standard web components. They are used to create reusable components that can be used in any web page.

Web components only live within the html body. For content within the head or a full html page please see layouts.

Rocket uses lit for layouts so using it for components as well makes it easy to switch back and forth. However any web component code/library should work.

Defining a component

A lit component typically looks like this:

import { html, css, LitElement } from 'lit';

class RocketGreeting extends LitElement {
  static properties = {
    count: { type: Number },
  };

  constructor() {
    super();
    this.count = 1;
  }

  render() {
    return html`
      <slot></slot>
      ${new Array(this.count).fill('🚀')}
    `;
  }

  static styles = css`
    :host {
      display: block;
    }
  `;
}
customElements.define('rocket-greeting', RocketGreeting);

We can now put this code in Rocket JavaScript, Markdown or Html pages.

```js server
// snippet
```

# Hello World

<rocket-greeting>Go</rocket-greeting>

will result in a server rendered output that does not load ANY JavaScript

Hello World

Go 🚀

If we would like to instead load the component and render it client side you can use the following code:

```js client
// snippet
```

# Hello World

<rocket-greeting>Go</rocket-greeting>

👆 e.g. we can replace the client/server hint on code blocks to decide where it gets executed.

If we want to use markdown within the component we can use the following code:

<rocket-greeting>

This **is** great!

</rocket-greeting>

Note the empty lines between html & markdown. They are necessary as this is how the markdown parser separates unprocessed html from markdown.

Manually Loading Components

We can define as many components as we want within a page but typically it's best to define them in separate files.

So we will move our component code into a new file 👉 /site/src/components/rocket-greeting.js

While importing components we decide if we want to server or client render.

👉 site/pages/index.rocket.js

// server side
import '../src/components/rocket-greeting.js';

// client side
export default () =>
  html` <script type="module" src="../src/components/rocket-greeting.js"></script> `;

👉 site/pages/index.rocket.html

<!-- server side -->
<script type="module" server>
  import '../src/components/rocket-greeting.js';
</script>

<!-- client side -->
<script type="module">
  import '../src/components/rocket-greeting.js';
</script>

👉 site/pages/index.rocket.md

## server side

```js server
import '../src/components/rocket-greeting.js';
```

## client side

```js client
import '../src/components/rocket-greeting.js';
```

Automatically Load and Register Components

Instead of manually loading and registering components we can let rocket handle it for us.

First we need to adjust our component file

  • Export the class
  • Get rid of the registration side effect
  • Rename the file rocket-greeting.js => RocketGreeting.js
- class RocketGreeting extends LitElement {
+ export class RocketGreeting extends LitElement {
  ...
  }
- customElements.define('rocket-greeting', RocketGreeting);

And then in our page we can export components where we define tag name, a "globally" working import and the class name.

export const components = {
  'rocket-greeting': 'my-pkg/src/components/RocketGreeting.js::RocketGreeting',
};

Often themes/presets will come with multiple components and a convenient way to add them to Rocket components. Typically we will spread those components in to our components object.

import { rocketComponents } from '@rocket/components/components';
import { sparkComponents } from '@rocket/spark/components';

export const components = {
  ...rocketComponents,
  ...sparkComponents,
  'rocket-greeting': 'my-pkg/src/components/RocketGreeting.js::RocketGreeting',
};

Site level components

Generally it make sense to put this export within site/pages/recursive.data.js. Doing so means the component will be automatically available for all pages.

Let me repeat that:

  • We define / spread in all components we wanna use within the whole website and then we never have to worry about it again.
  • Rocket will handle if and when the loading and registration should happen. Be it client or server side or both in a very specific timing via hydration.
  • Rocket will only load the components that are actually used which means that components can be a huge list without any performance impact.

Site level components are implemented in the same way as the Data Cascade e.g. by injecting those components into your file header. This means that we know all the components and where they are from by looking at the file header.

It may look something like this

export async function registerCustomElements() {
  // server-only components
  // prettier-ignore
  customElements.define('inline-notification', await import('@rocket/components/components/InlineNotification').then(m => m.InlineNotification));
  // hydrate-able components
  customElements.define(
    'my-counter',
    await import('#src/components/MyCounter.js').then(m => m.MyCounter),
  );
  // client-only components
  // 'rocket-search': () => import('@rocket/search/web').then(m => m.RocketSearch),
}

If we wish to disable or override a component then we can do so via

// the automatic import will contain something like this
import { components as originalComponents } from '../path/to/recursive.data.js';

export const components = {
  ...originalComponents, // keep all the original components
  'rocket-greeting': false, // no automatic handling of this component
  'other-component': 'my-pkg/src/components/OverrideOther.js::OverrideOther', // override the default component
};

Hydration

Components that do not have any interactivity will never need to be hydrated so they may be imported statically on the server side. All other components should be handled via the components object to enable handling of loading and registration.

Doing so enables hydration based on attributes on the component.

<rocket-greeting loading="hydration"></rocket-greeting>

See the Hydration Docs for more information.