Martin McKeaveney
Co-Founder and CTO @ Budibase
"Low-code is a visual approach to software development. With low-code, you can abstract and automate every step of the application lifecycle to streamline delivery of a variety of solutions. "
Budibase is an open-source low code platform built for developers - designed for speed and customisation.
Visually build the definition for an app
Read Definition -> Application
BuilderĀ -> Client Library -> Svelte Web App
Styling and Design System
Builder Routing
Development
Builder UI, Client Applications
Our entire design system is built using Spectrum CSS and svench
Screens
Layouts
Components
{
"_id": "screen_4fdbcf20029444ef8de16444da0f9801",
"_rev": "1-c1d08db5275a1237bfe4559efd68190a",
"description": "",
"url": "",
"layoutId": "layout_public_master",
"props": {
"_instanceName": "LoginScreenContainer",
"_id": "5beb4c7b-3c8b-49b2-b8b3-d447dc76dda7",
"_component": "@budibase/standard-components/container",
"_styles": {
"normal": {
"flex": "1 1 auto",
"display": "flex",
"flex-direction": "column",
"justify-content": "center",
"align-items": "center"
},
"hover": {},
"active": {},
"selected": {}
},
"_transition": "fade",
"type": "div",
"_children": [
{
"_id": "781e497e-2e7c-11eb-adc1-0242ac120002",
"_component": "@budibase/standard-components/login",
"_styles": {
"normal": {
"padding": "64px",
"background": "rgba(255, 255, 255, 0.4)",
"border-radius": "0.5rem",
"margin-top": "0px",
"box-shadow": "0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)",
"font-size": "16px",
"font-family": "Inter",
"flex": "0 1 auto"
},
"hover": {},
"active": {},
"selected": {}
},
"logo": "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg",
"title": "Log in to Some App",
"buttonText": "Log In",
"_children": [],
"_instanceName": "Login"
}
]
},
"routing": {
"route": "/",
"roleId": "PUBLIC"
},
"name": "login-screen"
}<script>
import { getContext } from "svelte"
import Router from "svelte-spa-router"
import { routeStore } from "../store"
</script>
<div use:styleable={$component.styles}>
<Router routes={config.routes} />
</div>"button": {
"name": "Button",
"description": "A basic html button.",
"icon": "ri-share-box-line",
"styleable": true,
"settings": [
{
"type": "text",
"label": "Text",
"key": "text"
},
{
"type": "boolean",
"label": "Disabled",
"key": "disabled"
},
{
"type": "event",
"label": "On Click",
"key": "onClick"
}
]
}
}- Components are generally presentational
- Data is fetched by a utility component called a Data Provider
- Fetches data on mount and keeps it up to date
<script>
$: fetchData(dataSource)
$: filteredRows = filterRows(allRows, filter)
$: sortedRows = sortRows(filteredRows, sortColumn, sortOrder)
$: rows = limitRows(sortedRows, limit)
$: getSchema(dataSource)
$: actions = [
{
type: ActionTypes.RefreshDatasource,
callback: () => fetchData(dataSource),
metadata: { dataSource },
},
]
$: dataContext = {
rows,
schema,
rowsLength: rows.length,
loading,
loaded,
}
</script>
<Provider {actions} data={dataContext}>
<slot />
</Provider>{
"repeater": {
"name": "Repeater",
"description": "A configurable data list that attaches to your backend tables.",
"icon": "ri-list-check-2",
"styleable": true,
"hasChildren": true,
"settings": [
{
"type": "dataProvider",
"label": "Data",
"key": "dataProvider"
},
{
"type": "text",
"label": "Empty Text",
"key": "noRowsMessage",
"defaultValue": "No rows found."
},
{
"type": "filter",
"label": "Filtering",
"key": "filter"
}
],
"context": {
"type": "schema"
}
}
}
<script>
// Provide contexts
setContext("sdk", SDK)
setContext("component", writable({}))
setContext("context", createContextStore())
// Load app config
onMount(async () => {
await initialise()
await authStore.actions.fetchUser()
})
// Register this as a refreshable datasource so that user changes cause
// the user object to be refreshed
$: actions = [
{
type: ActionTypes.RefreshDatasource,
callback: () => authStore.actions.fetchUser(),
metadata: { dataSource: { type: "table", tableId: TableNames.USERS } },
},
]
</script>
{#if loaded && $screenStore.activeLayout}
<Provider key="user" data={$authStore} {actions}>
<Component definition={$screenStore.activeLayout.props} />
<NotificationDisplay />
</Provider>
{/if}
<script>
import * as ComponentLibrary from "@budibase/standard-components"
export let definition = {}
// Props that will be passed to the component instance
let componentProps
// Get contexts
const context = getContext("context")
// Create component context
const componentStore = writable({})
setContext("component", componentStore)
// Extract component definition info
$: constructor = getComponentConstructor(definition._component)
$: children = definition._children || []
$: id = definition._id
$: updateComponentProps(definition, $context)
</script>
{#if constructor && componentProps}
<svelte:component this={constructor} {...componentProps}>
{#if children.length
{#each children as child (child._id)}
<svelte:self definition={child} />
{/each}
{/if}
</svelte:component>
{/if}
Production ready
No need for endless libraries
Svelte is extremely powerful for building low code and no code platforms
Svelte as bytecode