blade.im
289

Components

Link

Used to render a clickable link that forwards the user to a different page.

For maximum ease of use, the component also supports hash links (such as #test) and external links (starting with a protocol such as https:// or http://), however, in those cases, a simple anchor element with no event handlers will be rendered.

import { Link } from 'blade/components';

<Link href="/another-page">click me</Link>

Props

href
string
required

A string or URL object containing the pathname and search params of the destination page.

segments
object

An object containing key/value pairs of dynamic segments that should be used to populate the href. For example, if the href is /teams/[handle] and the segments prop is { handle: 'engineering' }, the destination page would be /teams/engineering.

prefetch
boolean
default:"true"

A boolean indicating whether the link should be prefetched. Read more about prefetching below.

  • Additionally, all attributes of anchor elements are supported as props of Link and will be forwarded to the underlying auto-generated anchor element.

Prefetching

On desktop, when a cursor enters a Link element (hover), the destination page will be loaded by Blade in the background, to ensure that, by the time the page transition happens (when the element is clicked), the page transition will be instant.

Similarily, on mobile devices, the destination page will be prefetched upon the start of the touch action, meaning when the screen is pressed. When the touch action ends (e.g. the finger is lifted), the transition begins.

In the future, links will also be prefetched when the page that contains them is loaded.

Form

Storing data in Blade is done by executing write queries with the useMutation hook. It makes it possible to construct any kind of logic for writing records, including building forms.

If you are specifically implementing a form, Blade also offers a native Form component, which makes building forms even easier. The component invokes useMutation internally.

For example, it could look like this:

import { Form, Input } from 'blade/components';

<Form model="account" redirect="/dashboard">
  <Input type="text" name="email" placeholder="Email" />
  <Input type="text" name="password" placeholder="Password" />

  <button>Submit</button>
</Form>

If the button at the end of the form is pressed, the following query will be executed, with xxx being the values that were provided in the inputs.

add.account.with({
  email: 'xxx',
  password: 'xxx'
});

As a result, a new record for the account model will be added with the provided values, and the page will change to /dashboard afterward.

Due to how useMutation is designed, the form submission will be executed with maximum efficiency: The write query will be executed in the same database transaction as the read queries of the /dashboard page, and only the /dashboard page will be rendered — not the current page.

To avoid changing the page after the form submission, simply leave out the redirect property.

Updating Records

To address an existing record using the Form component instead of creating a new one, you can pass a set prop with field values that should be used to resolve the existing record, before that record will then be updated automatically.

const { account } = use.session.using(['account']);
const { email } = account;
// ^ elaine@site.co

<Form model="account" set={{ email: account.email }}>
  <Input type="text" name="password" placeholder="Password" />
  <button>Submit</button>
</Form>

When submitted, the form above would then cause the following query to be executed, with xxx being the values that were provided in the inputs:

set.account({
  with: { email: 'elaine@site.co' },
  to: { password: 'xxx' }
});

As you can see, all that the set prop does is switch the write query from type "add" to type "set" and update its query instructions accordingly. That's it.

Deleting Records

If you would like to remove the target record instead of updating it, you can do so with the same syntax as shown above, except that you can then use a prop called remove instead of set.

<Form model="account" remove={{ email: '...' }}>...</Form>

Sophisticated Layouts

By default, the Form component automatically generates a form element in the DOM. That means both the input elements and the submit button must be present within that element, since that's how the form element works in HTML.

If your layout is significantly more complex, however, you might want the form element and the submit button to exist in two entirely different places. For example, you might want the form to exist in the content area of your layout, whereas the submit button might sit in the sidebar of your layout.

In such cases, you can set a noElement prop on the Form component, which will prevent the Form component from generatign the form element on its own. You then need to import a separate component, which will act as the form element.

Additionally, you then also need to handle the submission of the form yourself. In the example below, we are adding a custom SubmitButton that handles the form submission.

import { useContext } from 'react';
import { Form, FormElement, FormContext, Input } from 'blade/components';

const SubmitButton = () => {
  const form = useContext(FormContext);
  return <button onClick={() => form.submit()}>Submit</button>;
};

<Form model="account" noElement>
  <FormElement>
    <Input type="text" name="email" placeholder="Email" />
    <Input type="text" name="password" placeholder="Password" />
  </FormElement>

  <SubmitButton />
</Form>

Note that, if you choose to place the submit button outside FormElement, Blade will not be able to apply progressive enhancement for your form, meaning that the form will only be possible to submit once the JavaScript bundles have been downloaded.

Props

model
string
required

The slug (singular) of the Blade model that should be addressed.

set
object

An object containing fields and values that should be used to resolve an existing record, which will result in that record being updated. If the prop is omitted, a new record will be created instead.

remove
object

An object containing fields and values that should be used to resolve an existing record, which will result in that record being removed. If the prop is omitted, a new record will be created instead.

redirect
string

The path of a page that should be rendered after the form has been submitted. Just like the redirect option available for write queries in the useMutation hook, this prop can contain segments that should be replaced with the values of fields from the resulting record. For example, /accounts/{0.handle} would redirect to /accounts/elaine if the handle field contains the value elaine. Since this relies on the fields of the resulting record, you don't need to use the fields that are part of the form. Just use any field of the record.

disabled
boolean

Whether the form can be used, or not.

disabledWhileWaiting
boolean
default:"true"

Whether the disabled prop should automatically be set to true while the form is being submitted.

If your form is designed to be submitted several times in a row, setting this prop to true will cause the values of all inputs to be cleared after the form has been submitted successfully.

onSuccess
function

A function that will be called once the form has been submitted successfully, with the first argument being the resulting record.

onError
function

A function that will be called once the form has failed to be submitted, with the first argument being the resulting error that prevented the record from being saved successfully.

An array of field slugs that should be excluded from the final query, unless their value is truthy. This is useful for cases where the default value of an input inside the form is, for example, null, and the field should only be stored if it receives a string value.

database
string

The name of a specific database for which the query should be executed.

If one of the fields submitted with the form is used in the URL, this prop will ensure that the URL of the page automatically gets updated every time the value of the field changes. Specifically, an object containing param (the param in the URL, accessible via useParams) and field (the matching field on the record) must be provided. The URL will automatically be changed if the value of field changes. You can also provide formatAs: 'dash-case' to auto-format the value as dash-case.

When using the recordSlug prop, the Form component will consider new as a record slug in the URL to be a new (non-existing record). For example, the URL of the page for creating a new "team" record (if you have such a model defined) could then be /teams/new with the page name being /teams/[handle]. If you would like to use a value other than new for this purpose, set it in the prop right here.

including
string[]

An array of slugs of Link fields, which whose records should be resolved and provided to as part of the resulting record in onSuccess. This makes it possible to avoid having to run additional read queries after submitting a form. For example, if a Link field with the slug test is resolved, the record provided as the first argument of onSuccess will have a test property containing the linked record.

noElement
boolean

By default, a form element will be added for you in the DOM. To avoid this and ensure that the Form component thereby does not add any DOM elements whatsoever, you can set this prop to true.

Input

Provides the same functionality as the HTML Input element, but with a few additional props for ease of use in combination with the Form component.

import { Input } from 'blade/components';

<Input type="email" name="email" placeholder="Email" />

Props

Any default HTML prop is supported. Additionally the following:

target
boolean

If the prop is set, the value of the input will not be stored in the target record. Instead, the value will be used to resolve the target record. For example, if the input has the name email and the target prop is defined, Blade will try to resolve a record with the matching value of the email field, and then store all other input values in that record.

Image

Building a fast web application that instantly provides joy to its users whenever it is accessed (rather than introducing delays) typically requires paying attention to a number of different components.

import { Image } from 'blade/components';

<Image src="/juri.png" width={100} height={100} alt="A picture of Juri" />

Blade aims to automatically provide you with the optimizations you need to guarantee the fastest experience possible, additionally letting you fine-tune those improvements where necessary.

One of those optimizations is that images are displayed instantaneously, which Blade ensures via a number of different features available for images (image MIME type data) stored in the Blob field type.

Image Metadata

When retrieving records that contain fields of type “Blob”, you will be provided with a value that looks similar to the following for each of those fields:

export interface Blob {
  key: string;
  /** The URL of the binary object. */
  src: string;
  meta: {
    /** The byte size of the binary object. */
    size: number;
    /** The pixel width of the image, if the binary object is an image. */
    width: number;
    /** The pixel height of the image, if the binary object is an image. */
    height: number;
    /** The MIME type of the binary object. */
    type: string;
  };
  /** A base64 encoded preview to display while the image is loading, if the binary object is an image. */
  placeholder: {
    base64: string;
  };
}

Image Placeholders

If you are building a web application, in order to ensure the smoothest possible experience for the people making use of your application, we strongly advice displaying a low-quality blurred version of every image, until the full image has finished loading.

Like this, you can avoid empty white gaps in your page, and instead always display something that at least remotely resembles what the image will look like, until the image is available in its entirety. At this point the image can overlay the blurred low-quality version.

The Image component does this automatically, if you provide it with a placeholder property, which you can obtain directly from the respective Blob field.

Image Performance

To ensure that the final image finishes loading as quickly as possible, it is recommended to have its size automatically optimized by Blade, or your deployment provider.

Other additional options can be used as well, in order to guarantee the most ideal experience possible:

w
number

The desired pixel width of the image.

h
number

The desired pixel height of the image.

q
number

The desired quality of the image (number between 1 and 100).

fm
avif | webp | png | jpeg

The file format of the image.

fit
cover | contain | inside | outside | fill

How the image should be fit to its frame.

blur
number

The strength of a gaussian blur effect applied to the image (number between 0.3 and 1000).

Download

By default, all images are opened inside the browser when accessing them. If you wish to download them when accessing the image URL, add ?download=true or download=desired-filename.png to the URL. This will instruct browsers (set the Content-Disposition header to attachment) to download the image.