Writing Stories
Before flipbook can discover your Stories, you need a Storybook. A Storybook is any ModuleScript with a .storybook
extension. It acts as the topmost configuration for each collection of Stories in your project.
Relatedly, a Story is any ModuleScript with a .story
extension, typically parented as a sibling to the UI component it renders.
Stories are what you will be working with the most. The Storybook is simply what tells flipbook how to find them and render them.
Getting started
A Storybook can be parented anywhere in the experience. The only requirement is that it defines a storyRoots
array so flipbook knows where to search for Stories.
The simplest Storybook looks like this:
return {
storyRoots = {
script.Parent,
},
}
And here's an example of a Story that renders a TextButton:
return {
story = function()
local button = Instance.new("TextButton")
button.Text = "Button"
button.TextSize = 16
button.Font = Enum.Font.BuilderSansExtraBold
button.TextColor3 = Color3.fromRGB(50, 50, 50)
button.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
button.BorderSizePixel = 0
button.Size = UDim2.fromOffset(200, 40)
button.Activated:Connect(function()
print("clicked")
end)
return button
end,
}
In the flipbook plugin, opening the Button story will render out the component.
From there, making changing to the Story will live-reload the rendered button.
To connect it back to Studio, these files could simply be stored in ReplicatedStorage as ModuleScripts like so:
Using frameworks
By default, flipbook uses a function-based renderer with support for Roblox Instances to get you up and running. Simply returning an Instance allows flipbook to manage the creation and destruction of that Instance so you don't leak memory while working.
flipbook also has built-in support for UI libraries like React and Fusion. The full list can be seen on the Frameworks page.
You can tell flipbook to use a particular UI library by passing in the packages
object. Here's an example with React:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local React = require(ReplicatedStorage.Packages.React)
local ReactRoblox = require(ReplicatedStorage.Packages.ReactRoblox)
return {
story = function()
return React.createElement("TextButton", {
Text = "Click Me",
TextSize = 16,
Font = Enum.Font.BuilderSansExtraBold,
TextColor3 = Color3.fromRGB(50, 50, 50),
BackgroundColor3 = Color3.fromRGB(255, 255, 255),
BorderSizePixel = 0,
Size = UDim2.fromOffset(200, 40),
[React.Event.Activated] = function()
print("clicked")
end,
})
end,
packages = {
React = React,
ReactRoblox = ReactRoblox,
},
}
It can be tedious to supply the packages
object in each Story module, which is why it is more common to add them globally in the Storybook so that all Stories can render with the UI library you use across your project.
The following example splits out the body of the story to a ReactButton component and offloads the definition of packages
to the Storybook:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local React = require(ReplicatedStorage.Packages.React)
local function ReactButton(props: {
text: string,
onActivated: () -> (),
})
return React.createElement("TextButton", {
Text = props.text,
TextSize = 16,
Font = Enum.Font.BuilderSansExtraBold,
TextColor3 = Color3.fromRGB(50, 50, 50),
BackgroundColor3 = Color3.fromRGB(255, 255, 255),
BorderSizePixel = 0,
Size = UDim2.fromOffset(200, 40),
[React.Event.Activated] = props.onActivated,
})
end
return ReactButton
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local React = require(ReplicatedStorage.Packages.React)
local ReactButton = require(script.Parent.ReactButton)
return {
story = function()
return React.createElement(ReactButton, {
text = "Click Me",
onActivated = function()
print("click")
end,
})
end,
}
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local React = require(ReplicatedStorage.Packages.React)
local ReactRoblox = require(ReplicatedStorage.Packages.ReactRoblox)
return {
name = "React",
storyRoots = {
script.Parent,
},
packages = {
React = React,
ReactRoblox = ReactRoblox,
},
}
Stories can individually override the global packages
so if you need to use another UI library for a particular Story, you can do that.