Customizing Output

Customizing Output

Components

Zipper ships with a handful of pre-built UI components that allow you to customize the output of your applet. These components should be described as JSON in the output of your handler functions. Fortunately, Zipper provides a handy method that generates the necessary JSON for you: Zipper.Component.create().

Stacks

Based on flexbox containers, stacks let you easily control the layout of your output by applying rows or columns with evenly spaced elements.

Zipper.Component.create({
	/**
  * The type of component you want to create
  */
	type: 'stack';
 
	/**
  * The props of the stack component:
  * - direction: the flex direction of
  * - divider: show a divider between elements in the stack
  * - align: shorthand for the align-items CSS property
  */
  props: {
    direction: 'row' | 'column';
    divider?: boolean;
    align?: string;
  };
 
	/**
  * You can pass through an array of elements that will be
  * rendered within the stack. Children can be any JSON that
  * Zipper would typically render, including other Components
  * and Actions
  */
  children: Array<Serializable | Component>;
})

Links

The link component simply renders an a tag.

Zipper.Component.create({
	/**
  * The type of component you want to create
  */
  type: 'link';
 
	/**
  * The props of the stack component:
  * - href: the url you want to link to
  * - target: whether the link should open in a new tab
  */
  props: {
    href: string;
    target?: '_blank' | '_self';
  };
 
	/**
  * The text of the link
  */
  text: string;
})

Sections

An applet is able to render its output in two places:

  1. The main section (where output is rendered when the app is run)
  2. In a modal (which must be triggered via an Action.

Each of these places has two sections: primary and expanded. The expanded section opens below the primary section and must be triggered via an Action. The expanded section should be thought of as a way to peek into information in the primary section.

If you output to the same place and section multiple times, Zipper will replace the content but allow the user to navigate back through the stack.

Rendering UI from JSON

Zipper will automatically convert the JSON output of your handler function into rendered UI.

Arrays

Arrays will be converted to tables with sortable columns. If your arrays contain only primitives, then they can also be viewable as cards. If your array has more complex objects and arrays, those will be rendered within the table.

Objects

Objects will be rendered in Zipper’s object explorer, allowing you to expand and collapse keys. Zipper can also render nested objects and arrays.

HTML

If you return an HTML string, Zipper will simply render HTML.

Markdown

By default, returning a string will render your string in Markdown. You can also explicitly send Markdown as text to any part of your applet by using the md tag. For example:

const plaintext = 'regular text';
const markdown = md`
# Heading
 
## Subheading
 
Some _rich_ _text_ (here)[https://zipper.works]
 
1.  one
2.  two
3.  three
`;
 
return { plaintext, markdown };

The above code will render an object with both plaintext and markdown text.

Advanced: JSX Output for Custom UI

Zipper also supports JSX for rendering basic HTML and special Zipper Components. At this time, Zipper does not support full client-side React/Preact. However, you can compose Actions, Components, and HTML layout together.

For example, you can use the Stack Components (as Row and Column ) to build out a layout with text, HTML, and Actions (as Button and Dropdown ).

return (
  <>
    <h1>Title</h1>
    <h2>Subtitle</h2>
    <Markdown>Some **rich** _text_ here</Markdown>
    <Stack>
      <Row>
        <Column>
          <Button
            showAs="modal"
            path="greeting"
            run={true}
            inputs={{ world: 'Earth!!!' }}
          >
            earth
          </Button>
        </Column>
        <Column>
          <Dropdown
            options={{
              world: [
                {
                  label: 'mars',
                  value: 'Mars!!!',
                },
                {
                  label: 'venus',
                  value: 'Venus!!!',
                },
              ],
            }}
            path="greeting"
            showAs="modal"
            text="OK"
          />
        </Column>
      </Row>
      <Row>
        <Column>three</Column>
        <Column>
          <Link href="https://google.com/">Go to Google</Link>
        </Column>
      </Row>
    </Stack>
  </>
);

This will return a UI that looks like this:

JSX Custom UI

This would save you from writing all this JSON by hand:

[
  {
    "$zipperType": "Zipper.Component",
    "type": "html.h1",
    "props": null,
    "children": ["Title"]
  },
  {
    "$zipperType": "Zipper.Component",
    "type": "html.h2",
    "props": null,
    "children": ["Subtitle"]
  },
  {
    "$zipperType": "Zipper.Component",
    "type": "markdown",
    "props": {},
    "children": ["Some **rich** _text_ here"]
  },
  {
    "$zipperType": "Zipper.Component",
    "type": "stack",
    "props": {},
    "children": [
      {
        "$zipperType": "Zipper.Component",
        "type": "stack",
        "props": {
          "direction": "row"
        },
        "children": [
          {
            "$zipperType": "Zipper.Component",
            "type": "stack",
            "props": {
              "direction": "column"
            },
            "children": [
              {
                "$zipperType": "Zipper.Action",
                "actionType": "button",
                "showAs": "modal",
                "path": "greeting",
                "run": true,
                "inputs": {
                  "world": "Earth!!!"
                },
                "text": "earth"
              }
            ]
          },
          {
            "$zipperType": "Zipper.Component",
            "type": "stack",
            "props": {
              "direction": "column"
            },
            "children": [
              {
                "$zipperType": "Zipper.Action",
                "actionType": "dropdown",
                "options": {
                  "world": [
                    {
                      "label": "mars",
                      "value": "Mars!!!"
                    },
                    {
                      "label": "venus",
                      "value": "Venus!!!"
                    }
                  ]
                },
                "path": "greeting",
                "showAs": "modal",
                "text": "OK",
                "children": []
              }
            ]
          }
        ]
      },
      {
        "$zipperType": "Zipper.Component",
        "type": "stack",
        "props": {
          "direction": "row"
        },
        "children": [
          {
            "$zipperType": "Zipper.Component",
            "type": "stack",
            "props": {
              "direction": "column"
            },
            "children": ["three"]
          },
          {
            "$zipperType": "Zipper.Component",
            "type": "stack",
            "props": {
              "direction": "column"
            },
            "children": [
              {
                "$zipperType": "Zipper.Component",
                "type": "link",
                "props": {
                  "href": "https://google.com/"
                },
                "children": ["Go to Google"]
              }
            ]
          }
        ]
      }
    ]
  }
]

Composing components

With JSX and React-style components, you can make incredibly powerful and custom UIs with very little code. Try composing different patterns into components to build truly custom UIs.

type Props = { title: string; subtitle: string; };
 
const MyHeaderComponent = (props: Props) => (
	<Row>
		<h1>{title}</h1>
		<h3>{subtitle}<h3>
	</Row>
);
 
export function handler() {
	return <MyHeaderComponent title="Hello" subtitle="world" />;
}
Sign inJoin the beta
© 2023 Zipper, Inc. All rights reserved.