Use Webiny Page Builder when you need to:
Build stunning landing pages in seconds
Build dynamic pages, such as product pages, blogs, training courses and more
Bring your own custom interactive components
Provide your content creators with a library of ready-made templates and building blocks
Deliver pages at scale to large audiences
Webiny’s advanced page builder brings you a seamless interface to design, edit, and publish compelling pages with ease. Say goodbye to the technical hurdles and unleash your creativity in crafting digital experiences that resonate with your audience. Discover a no-code solution that’s tailored for both developers and non-developers, enabling you to manage your web content flawlessly.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras iaculis et libero eget eleifend. Vestibulum felis ipsum, aliquet eget semper at, eleifend non nisl.
Create your own library of no-code page templates so you never start with a blank page again.
Create individual building blocks, like call-to-action sections, and features sections, and re-use them across multiple pages.
Control who can use the pre-defined building blocks, vs who can edit them, ensuring the content always meets your strict quality requirements.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras iaculis et libero eget eleifend. Vestibulum felis ipsum, aliquet eget semper at, eleifend non nisl.
We used the best parts of serverless infrastructure to architect Webiny Page Builder - it easily handles very large volumes of traffic, and flash crowds, and no matter what, your website will not go down.
We didn’t just optimize the page delivery, we also ensured your content editors always have a system that’s fast and responsive.
The Page Builder also has many optimizations under the hood, from delivering only the needed CSS styles, optimizing and resizing images automatically to CDN caching.
The end-to-end platform that Webiny provides solves challenges around data ownership, customizations,
infrastructure cost, scalability & reliability and helps you manage the full content lifecycle.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras iaculis et libero eget
eleifend. Vestibulum felis ipsum, aliquet eget semper at, eleifend non nisl.
Host thousands of projects from a single instance
Architected to be extended and customized.
Your data under your terms. A privacy-focused CMS.
Webiny runs on highly-scalable fault-tolerant serverless services.
Build new features, change existing ones, or create whole new apps.
No-code suite of solutions helping you create, manage and distribute content.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras iaculis et libero eget eleifend. Vestibulum felis ipsum, aliquet eget semper at, eleifend non nisl.
Webiny Page Builder delivers pre-rendered pages so any search bot can fully index all of the content on your page, you don’t need to do anything extra.
The editor allows you to build pages for all different device sizes, from desktop to mobile.
Integrations with your current analytics tools, A/B testing tools, and advertising tools are fully supported.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras iaculis et libero eget eleifend. Vestibulum felis ipsum, aliquet eget semper at, eleifend non nisl.
Webiny is architected as an open-source product, it comes with a plugin system, lifecycle events, hooks, and more that you can use to make it truly fit your needs.
If you can write it as a React component, you can use it inside the Page Builder, no matter if it’s a static component or a component that re-hydrates and talks to 3rd party APIs.
You can extend features like the formatting options in the rich text editor to adding new fields inside the page settings form.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras iaculis et libero eget eleifend. Vestibulum felis ipsum, aliquet eget semper at, eleifend non nisl.
Type definitions across the whole project to help you get around.
Using Webiny CLI you can propagate code trough different environments, like dev, prod.
Change existing GraphQL resolvers, or add new ones in a few lines of code.
Webiny is deployed inside your AWS account following all security best practices.
You can integrate any of your existing IdPs.
Control and modify your infrastructure through Pulumi IaC.
70+
Contributors
2,700+
Developers on Slack
7,200+
GitHub stars
Webiny is architected from the ground up to be adopted, extended and built upon. You can literally change every part of Webiny, in a safe and maintainable way.
Lifecycle events are added on top of folder actions which can be used to trigger custom functions.
12new ContextPlugin<AcoContext>(async context => {3 context.aco.onFolderAfterCreate.subscribe(async ({ folder }) => {4 // Trigger custom function here5 console.log('Folder created', folder.title);6 });7});8
When selecting multiple entries, you can register custom bulk actions that the user can perform.
12<ContentEntryListConfig>3 <BulkAction4 name={"copy-json"}5 element={<ActionCopyJson />}6 modelIds={["article"]}7 />8</ContentEntryListConfig>9
You can register custom filters to filter the entries in the list.
12<ContentEntryListConfig>3 <Browser.Filter name={"demo-filter"} element={<span>Demo Filter</span>} />4</ContentEntryListConfig>5
Configure on a per-model basis which columns are shown in the list view.
12<ContentEntryListConfig>3 <Browser.Column4 name={"price"}5 header={"Price"}6 modelIds={["property"]}7 />8</ContentEntryListConfig>9
Control how a column is rendered in the list view.
12export const CellPrice = () => {3 // You can destructure child methods to make the code more readable and easier to work with.4 const { useTableRow, isFolderRow } = ContentEntryListConfig.Browser.Table.Column;5 // useTableRow() allows you to access the entire data of the current row.6 const { row } = useTableRow();78 // isFolderRow() allows for custom rendering when the current row is a folder.9 if (isFolderRow(row)) {10 return <>{"-"}</>;11 }1213 const currency = new Intl.NumberFormat("en-US", {14 style: "currency",15 currency: row.currency // Let's use the currency defined in the entry.16 });1718 // Let's render the entry price.19 return <>{currency.format(row.price)}</>;20};21<ContentEntryListConfig>22 <Browser.Column23 name={"price"}24 header={"Price"}25 modelIds={["property"]}26 cell={<CellPrice />}27 />28</ContentEntryListConfig>29
Customize how search query works and how the input parameters are handled.
12export const CellPrice = () => {3 // You can destructure child methods to make the code more readable and easier to work with.4 const { useTableRow, isFolderRow } = ContentEntryListConfig.Browser.Table.Column;5 // useTableRow() allows you to access the entire data of the current row.6 const { row } = useTableRow();78 // isFolderRow() allows for custom rendering when the current row is a folder.9 if (isFolderRow(row)) {10 return <>{"-"}</>;11 }1213 const currency = new Intl.NumberFormat("en-US", {14 style: "currency",15 currency: row.currency // Let's use the currency defined in the entry.16 });1718 // Let's render the entry price.19 return <>{currency.format(row.price)}</>;20};21<ContentEntryListConfig>22 <Browser.Column23 name={"price"}24 header={"Price"}25 modelIds={["property"]}26 cell={<CellPrice />}27 />28</ContentEntryListConfig>29
Remove or register new actions that can be performed on an entry.
12<ContentEntryListConfig>3 <Browser.EntryAction4 name={"copy-json"}5 element={<CopyEntryData />}6 modelIds={["property"]}7 />8</ContentEntryListConfig>9
Register a custom element group
12export default {3 name: "pb-editor-element-group-webiny-website",4 type: "pb-editor-page-element-group",5 group: {6 title: "Webiny Website",7 icon: <Icon />8 }9} as PbEditorPageElementGroupPlugin;10
Register a custom element you can use to build your page
12const plugin = {3 name: "pb-render-page-element-space-x",4 type: "pb-render-page-element",5 elementType: "spaceX",6 render: SpaceX7} as PbRenderElementPlugin;8
Create advanced nestable elements and control where and how they can be nested
12const plugin = {3 name: "pb-render-page-element-child-example",4 type: "pb-render-page-element",5 elementType: "childExample",6 render: ChildExample,7 // Whitelist elements that can accept this element8 // (for drag&drop interaction)9 target: ["cell", "block"],10} as PbEditorPageElementPlugin;11
Create elements that are interactive and can also fetch data from external sources
12export const SpaceX = createRenderer(() => {3 // Let's retrieve the variables that were chosen by4 // the user upon dropping the page element onto the page.5 const { getElement } = useRenderer();6 const element = getElement<SpaceXElementData>();7 const { limit, offset, type } = element.data.variables;89 const [data, setData] = useState<Spacecraft[]>([]);1011 // This is where we fetch the data and store it into component's state.12 useEffect(() => {13 request(GQL_API_URL, QUERIES[type], {14 limit: parseInt(limit),15 offset: parseInt(offset)16 }).then(({ data }) => setData(data));17 }, [limit, offset, type]);1819 if (!data.length) {20 return <>Nothing to show.</>;21 }2223 return <>SpaceX has {data.length} rockets</>;24});2526const plugin = {27 name: "pb-render-page-element-space-x",28 type: "pb-render-page-element",29 elementType: "spaceX",30 render: SpaceX31} as PbRenderElementPlugin;32
Register custom plugins to define new style props, or remove existing style props
12export default {3 name: "pb-editor-page-element-style-settings-text",4 type: "pb-editor-page-element-style-settings",5 render({ options }) {6 return <TextSettings options={options} />;7 }8} as PbEditorPageElementStyleSettingsPlugin;9
Register custom attributes for your custom elements. Example, define which category of products will be listed inside your custom listing component.
12export default {3 name: "pb-editor-page-element-advanced-settings-carousel",4 type: "pb-editor-page-element-advanced-settings",5 elementType: "carousel",6 render() {7 return <CarouselItems />;8 }9} as PbEditorPageElementAdvancedSettingsPlugin;10
Remove page settings props you don't need. Create custom page settings props for your own needs.
12export default [3 // Add 'password' to the page settings types4 new GraphQLSchemaPlugin<Context>({5 typeDefs: /* GraphQL */ `6 extend type PbGeneralPageSettings {7 password: String8 }910 extend input PbGeneralPageSettingsInput {11 password: String12 }13 `14 }),15 // Subscribe to the page update event using the ContextPlugin.16 new ContextPlugin<PbContext>(({ pageBuilder }) => {17 // We are passing a custom event type to allow us to use the new 'password' field.18 pageBuilder.onBeforePageUpdate.subscribe<CustomEventParams>(({ page, input }) => {19 // Explicitly assign the field value from GraphQL input to the data that is used to update the page.20 page.settings.general.password = input.settings.general.password;21 });22 })23 ];24
Take over the publish button action and trigger a custom action. Or an action that happens before or and after the page publish event.
12new ContextPlugin<PbContext>(async context => {3 context.pageBuilder.onBeforePagePublish.subscribe(async ({ latestPage, page }) => {4 /**5 * For example, we do not allow a page which is not the latest one to be published.6 */7 if (latestPage.version > page.version) {8 throw new Error(`Page you are trying to publish is not the latest revision of the page.`);9 }10 });11 });12
Register a plugin to add a new file type handler to the file manager
12export default [3 new FileManagerFileTypePlugin({4 types: ["video/mp4"],5 render({ file }) {6 return (7 <div style={{ paddingTop: "40%" }}>8 <strong>My MP4</strong>9 <br />10 <span>{file.name}</span>11 <br />12 <span>{file.size} bytes</span>13 </div>14 );15 }16 })17];18
Register your own custom File Manager UI, useful if you want to use a different DAM system, something like Cloudinary, Dropbox, etc.
12const CustomFileManager = createDecorator(FileManagerRenderer, () => {3 return function FileManagerRenderer(props) {4 const setRandomImage = () => {5 const id = Date.now().toString();6 const image: FileManagerFileItem = {7 id,8 src: `https://picsum.photos/seed/${id}/200/300`,9 meta: [{ key: "source", value: "https://picsum.photos/" }]10 };11 if (props.multiple) {12 props.onChange && props.onChange([image]);13 } else {14 props.onChange && props.onChange(image);15 }16 props.onClose && props.onClose();17