README
@flayyer/variables
Helper module to create a schema
that enables Flayyer to display template's variables on https://flayyer.com for decks and templates.
yarn add @flayyer/variables
Some of our official free templates are using @flayyer/variables
:
- 🌠 flayyer.com/@/flayyer/simple-products
- Source-code: github.com/flayyer/flayyer-marketplace-simpleproducts
- 🌠 flayyer.com/@/flayyer/nice-typography
- Source-code: github.com/flayyer/flayyer-marketplace-nicetypography
- 🌠 flayyer.com/@/flayyer/branded
- Source-code: github.com/flayyer/flayyer-marketplace-brand
Here is what a template with an exported schema
looks like:
Usage
import { Variable as V, Static, Validator } from "@flayyer/variables";
/**
* Export `const schema = V.Object({})` to make variables visible on https://flayyer.com/
*/
export const schema = V.Object({
title: V.String({ description: "Displayed on https://flayyer.com" }),
description: V.Optional(V.String()),
image: V.Optional(V.Image({
description: "Image URL",
examples: ["https://flayyer.com/logo.png"],
})),
font: V.Optional(V.Font({
default: "Inter", // https://github.com/flayyer/use-googlefonts
})),
});
const validator = new Validator(schema);
// Remove line and TemplateProps<Variables> if using plain Javascript
type Variables = Static<typeof schema>;
export default function Template({ variables }: TemplateProps<Variables>) {
if (validator.validate(variables)) {
const title = variables["title"];
const description = variables["description"];
const image = variables["image"];
// ...
}
}
Optimistic usage
Using the same schema from the previous example, if you prefer a more optimistic validation you can use:
const {
data: { image, description, image }
} = validator.parse(variables);
// Optimistic path but variables might not be compliant with the schema.
Instead of (strict validation):
if (validator.validate(variables)) {
// Happy path with compliant variables
} else {
// Show empty or error to the user.
}
Useful types
Most common types with full Flayyer.com UI support are:
V.String
V.Integer
V.Number
for floatsV.Boolean
V.DateTime
,V.Date
, andV.Time
(V.DateTime
has the best compatibility)V.URL
V.Image
V.Font
V.ColorHex
V.Enum
V.EnumKeys
You should be able to cover most cases with these types.
E-commerce
For E-commerce templates you probably want to display the price and currency of a product. Currently we haven't defined a proper V.Price
and V.Currency
methods yet. We recommended sticking with price: V.Number
and currency: V.String
until we have enough information to create those methods.
Production example: https://github.com/flayyer/flayyer-marketplace-simpleproducts
Example:
import { Variable as V, Validator } from '@flayyer/variables';
export const schema = V.Object({
currency: V.Optional(
V.String({ default: 'USD', examples: ['USD', 'EUR'] }),
),
price: V.Optional(
V.Number({ examples: ['59.99'] }),
),
});
const validator = new Validator(schema);
// props are provided by our systems
export default function Template({ variables, locale }) {
const { data: { currency, price } } = validator.parse(variables);
const formatter = new Intl.NumberFormat(locale, {
style: 'currency',
currency: currency,
currencyDisplay: 'symbol'
});
if (Number.isFinite(price) && price === 0) {
return <p>Free</p>;
} else (Number.isFinite(price)) {
return <p>{formatter.format(price)}</p>
} else {
return null; // Do not display price if user sets input to null or blank,
}
}
Enums
TypeScript has the enum
type. This library can create a schema for these enums based on their keys or their values.
- Create enum schema with keys:
V.EnumKeys
- Create enum schema with values:
V.Enum
enum Alignment {
Y = "flex flex-col justify-center",
X = "flex flex-row justify-center",
}
const schema = V.Object({
keys: V.EnumKeys(Alignment, { default: "X" }),
values: V.Enum(Alignment, { default: Alignment.X }),
});
What is the difference? If you want to display the enum's key on the UI at Flayyer.com you should use V.EnumKeys
which is more clear to the user.
// Let the user pick between "X" or "Y" on Flayyer.com UI.
const schema = V.Object({
alignment: V.EnumKeys(Alignment, { default: "X" }),
});
Recommendations
JSON Schemas can be super complex and allow a lot of custom settings. In Flayyer.com we recommend sticking to a simple 1-level object for the better final user experience.
// ⚠️ Works via @flayyer/flayyer and code but not handled by Flayyer.com UI
export const schema = V.Object({
title: V.Object({
text: V.String(),
color: V.ColorHex({ default: "#FFFFFF" }),
font: V.Font({ default: "Inter" }),
})
});
/* https://flayyer.io/v2/tenant/deck/template?title[text]=Hello&title[font]=Roboto */
/* https://flayyer.ai/v2/project/_/title[text]=Hello&title[font]=Roboto/path */
// ✅ Recommended! works via @flayyer/flayyer and Flayyer.com UI
export const schema = V.Object({
title: V.String(),
titleColor: V.ColorHex({ default: "#FFFFFF" }),
titleFont: V.Font({ default: "Inter" }),
});
/* https://flayyer.io/v2/tenant/deck/template?title=Hello&titleFont=Roboto */
/* https://flayyer.ai/v2/project/_/title=Hello&titleFont=Roboto/path */
// It also requires shorter URLs to generate images.
Credits to https://github.com/sinclairzx81/typebox to enable creating a JSON Schema with an amazing developer experience.