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.
tip
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.
tip
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.