Landing pages are the most common application of integrating page models.
You can use the standard Page model. The Page model that Builder comes by default is named page. For this example, you'll want to make sure you have a couple of fields:
You can also optionally add additional fields for other metadata.
For landing pages in all frameworks:
Add this as a catchall page; for example, pages/[...page].tsx (or .tsx):
// pages/[...page].tsx
import React from "react";
import { useRouter } from "next/router";
import { BuilderComponent, builder, useIsPreviewing } from "@builder.io/react";
import { BuilderContent } from "@builder.io/sdk";
import DefaultErrorPage from "next/error";
import Head from "next/head";
import { GetStaticProps } from "next";
// Replace with your Public API Key
builder.init(YOUR_API_KEY);
// Define a function that fetches the Builder
// content for a given page
export const getStaticProps: GetStaticProps = async ({ params }) => {
// Fetch the builder content for the given page
const page = await builder
.get("page", {
userAttributes: {
urlPath: "/" + ((params?.page as string[])?.join("/") || ""),
},
})
.toPromise();
// Return the page content as props
return {
props: {
page: page || null,
},
// Revalidate the content every 5 seconds
revalidate: 5,
};
};
// Define a function that generates the
// static paths for all pages in Builder
export async function getStaticPaths() {
// Get a list of all pages in Builder
const pages = await builder.getAll("page", {
// We only need the URL field
fields: "data.url",
options: { noTargeting: true },
});
// Generate the static paths for all pages in Builder
return {
paths: pages.map((page) => `${page.data?.url}`).filter(url => url !== '/'),
fallback: 'blocking',
};
}
// Define the Page component
export default function Page({ page }: { page: BuilderContent | null }) {
const router = useRouter();
const isPreviewing = useIsPreviewing();
// If the page content is not available
// and not in preview mode, show a 404 error page
if (!page && !isPreviewing) {
return <DefaultErrorPage statusCode={404} />;
}
// If the page content is available, render
// the BuilderComponent with the page content
return (
<>
<Head>
<title>{page?.data?.title}</title>
</Head>
{/* Render the Builder page */}
<BuilderComponent model="page" content={page || undefined} />
</>
);
}
Tip: If you don't have an index route, such as pages/index.tsx , use pages/[[...page.tsx]] with two sets of square brackets. The two square brackets means that Builder handles the app's home page.
The single set of square brackets, in this tutorial, assumes that you are keeping your home page.
Add this as a catchall page; for example, pages/[...page].jsx (or .tsx):
// pages/[...page].tsx/**
// Catch all route to build and render content from Builder.io in SSG mode
import type { BuilderContent } from '@builder.io/sdk-react';
import {
Content,
fetchEntries,
fetchOneEntry,
isPreviewing,
} from '@builder.io/sdk-react';
import type { GetStaticProps } from 'next';
import DefaultErrorPage from 'next/error';
import Head from 'next/head';
import { useRouter } from 'next/router';
//TO DO: Add you public API Key here
const BUILDER_API_KEY = /* your API key here */;
// Define a function that fetches the Builder content for a given page
export const getStaticProps: GetStaticProps = async ({ params }) => {
const urlPath =
'/' +
(Array.isArray(params?.page) ? params.page.join('/') : params?.page || '');
// Fetch the builder content for the given page
const page = await fetchOneEntry({
apiKey: BUILDER_API_KEY,
model: 'page',
userAttributes: { urlPath },
});
return {
// Return the page content as props
props: { page },
// Revalidate the content every 5 seconds
revalidate: 5,
};
};
// Define a function that generates the
// static paths for all pages in Builder
export async function getStaticPaths() {
// Get a list of all pages in Builder
const pages = await fetchEntries({
apiKey: BUILDER_API_KEY,
model: 'page',
// We only need the URL field
fields: 'data.url',
options: { noTargeting: true },
});
// Generate the static paths for all pages in Builder
return {
paths: pages.map((page) => `${page.data?.url}`),
fallback: 'blocking',
};
}
// Define the Page component
export default function Page(props: { page: BuilderContent | null }) {
const router = useRouter();
const canShowContent = props.page || isPreviewing(router.asPath);
// If the page content is not available
// and not in preview/editing mode, show a 404 error page
if (!canShowContent) {
return <DefaultErrorPage statusCode={404} />;
}
// If the page content is available, render
// the BuilderComponent with the page content
return (
<>
<Head>
<title>{props.page?.data?.title}</title>
</Head>
{/* Render the Builder page */}
<Content model="page" content={props.page} apiKey={BUILDER_API_KEY} />
</>
);
}Tip: If you don't have an index route, such as pages/index.tsx , use pages/[[...page.tsx]] with two sets of square brackets. The two square brackets means that Builder handles the app's home page.
The single set of square brackets, in this tutorial, assumes that you are keeping your home page.
Create a components/builder.tsx file:
// components/builder.tsx
"use client";
import { ComponentProps } from "react";
import { BuilderComponent, useIsPreviewing } from "@builder.io/react";
import { BuilderContent, builder } from '@builder.io/sdk';
import DefaultErrorPage from "next/error";
type BuilderPageProps = ComponentProps<typeof BuilderComponent>;
// Replace with your Public API Key
builder.init(YOUR_API_KEY);
export function RenderBuilderContent(props: BuilderPageProps) {
// Call the useIsPreviewing hook to determine if
// the page is being previewed in Builder
const isPreviewing = useIsPreviewing();
// If `content` has a value or the page is being previewed in Builder,
// render the BuilderComponent with the specified content and model props.
if (props.content || isPreviewing) {
return <BuilderComponent {...props} />;
}
// If the `content` is falsy and the page is
// not being previewed in Builder, render the
// DefaultErrorPage with a 404.
return <DefaultErrorPage statusCode={404} />;
}
Notice that RenderBuilderContent is a component you'd make, for example:
"use client";
import { ComponentProps } from "react";
import { BuilderComponent, useIsPreviewing } from "@builder.io/react";
import DefaultErrorPage from "next/error";
type BuilderPageProps = ComponentProps<typeof BuilderComponent>;
export function RenderBuilderContent(props: BuilderPageProps) {
const isPreviewing = useIsPreviewing();
if (props.content || isPreviewing) {
return <BuilderComponent {...props} />;
}
return null;
}In app/[...page]/page.tsx paste in the following:
// Example file structure, app/[...page]/page.tsx
// You could alternatively use src/app/[...page]/page.tsx
import { builder } from "@builder.io/sdk";
import { RenderBuilderContent } from "../../components/builder";
// Replace with your Public API Key
builder.init(YOUR_API_KEY);
interface PageProps {
params: {
page: string[];
};
}
export default async function Page(props: PageProps) {
const model = "page";
const content = await builder
// Get the page content from Builder with the specified options
.get("page", {
fetchOptions: { next: { revalidate: 5 } },
userAttributes: {
// Use the page path specified in the URL to fetch the content
urlPath: "/" + (props?.params?.page?.join("/") || ""),
},
// Set prerender to false to return JSON instead of HTML
prerender: false,
})
// Convert the result to a promise
.toPromise();
return (
<>
{/* Render the Builder page */}
<RenderBuilderContent content={content} model={model} />
</>
);
}This example uses the following path configuration in tsconfig.json:
//tsconfig.json
...
"paths": {
"@/*": ["./*"]
}
...If you prefer to use the default src path for @/*, remember to adjust your RenderBuilderContent import path.
Tip: If you don't have an index route, such as app/index.tsx , use app/[[...page.tsx]] with two sets of square brackets. The two square brackets means that Builder handles the app's home page.
The single set of square brackets, in this tutorial, assumes that you are keeping your home page.
In the app/[...slug] directory, create a file called page.tsx and paste in the following:
// Example file structure, app/[...slug]/page.tsx
// You could alternatively use src/app/[...slug]/page.tsx//**
/**
* Quickstart snippet
* snippets/nextjs-app-dir-client/app/[[...slug]].tsx
* Uses @builder.io/sdk-react
*/
import {
Content,
fetchOneEntry,
getBuilderSearchParams,
isPreviewing,
} from '@builder.io/sdk-react';
interface PageProps {
params: {
slug: string[];
};
searchParams: Record<string, string>;
}
const PUBLIC_API_KEY = /* Put your Public API Key here */;
export default async function Page(props: PageProps) {
const urlPath = '/' + (props.params?.slug?.join('/') || '');
const content = await fetchOneEntry({
options: getBuilderSearchParams(props.searchParams),
apiKey: PUBLIC_API_KEY,
model: 'page',
userAttributes: { urlPath },
});
const canShowContent = content || isPreviewing(props.searchParams);
if (!canShowContent) {
return (
<>
<h1>404</h1>
<p>Make sure you have your content published at Builder.io.</p>
</>
);
}
return <Content content={content} apiKey={PUBLIC_API_KEY} model="page" />;
}import { useEffect, useState } from "react";
import { BuilderComponent, builder, useIsPreviewing } from "@builder.io/react";
// Put your API key here
builder.init(YOUR_API_KEY);
// set whether you're using the Visual Editor,
// whether there are changes,
// and render the content if found
export default function CatchAllRoute() {
const isPreviewingInBuilder = useIsPreviewing();
const [notFound, setNotFound] = useState(false);
const [content, setContent] = useState(null);
// get the page content from Builder
useEffect(() => {
async function fetchContent() {
const content = await builder
.get("page", {
url: window.location.pathname
})
.promise();
setContent(content);
setNotFound(!content);
// if the page title is found,
// set the document title
if (content?.data.title) {
document.title = content.data.title
}
}
fetchContent();
}, [window.location.pathname]);
// If no page is found, return
// a 404 page from your code.
// The following hypothetical
// <FourOhFour> is placeholder.
if (notFound && !isPreviewingInBuilder) {
return <FourOhFour/>
}
// return the page when found
return (
<>
{/* Render the Builder page */}
<BuilderComponent model="page" content={content} />
</>
);
}/** App.tsx or any JavaScript file you're using*/
import {
Content,
fetchOneEntry,
getBuilderSearchParams,
isPreviewing,
type BuilderContent,
} from '@builder.io/sdk-react';
import { useEffect, useState } from 'react';
// TO DO: add your Public API key
const BUILDER_API_KEY = /* your API key here */;
const MODEL_NAME = 'page';
// set whether you're using the Visual Editor,
// whether there are changes,
// and render the content if found
export default function App() {
const [notFound, setNotFound] = useState(false);
const [content, setContent] = useState<BuilderContent | null>(null);
// get the page content from Builder
useEffect(() => {
fetchOneEntry({
model: MODEL_NAME,
apiKey: BUILDER_API_KEY,
userAttributes: {
urlPath: window.location.pathname,
},
options: getBuilderSearchParams(new URL(location.href).searchParams),
})
.then((content) => {
if (content) {
setContent(content);
}
setNotFound(!content);
})
.catch((err) => {
console.log('Oops: ', err);
});
}, []);
// If no page is found, return
// a 404 page from your code.
if (notFound && !isPreviewing()) {
return <div>404</div>;
}
// return the page when found
return (
<Content content={content} model={MODEL_NAME} apiKey={BUILDER_API_KEY} />
);
}Tip: When deploying the React Gen 2 SDK to an edge runtime, you must use the @builder.io/sdk-react/edge import instead of @builder.io/sdk-react. This is necessary to avoid importing code that only works on NodeJS runtimes.
// ($slug)._index.tsx
import {
Content,
fetchOneEntry,
getBuilderSearchParams,
isPreviewing,
} from '@builder.io/sdk-react';
import type { LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { fetch as webFetch } from '@remix-run/web-fetch';
const apiKey = /* Add your Public API Key here */;
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
const urlPath = `/${params['slug'] || ''}`;
const page = await fetchOneEntry({
model: 'page',
apiKey: apiKey,
options: getBuilderSearchParams(url.searchParams),
userAttributes: { urlPath },
fetch: webFetch,
});
if (!page && !isPreviewing(url.search)) {
throw new Response('Page Not Found', {
status: 404,
statusText: 'Page not found in Builder.io',
});
}
return { page };
};
// Define and render the page.
export default function Page() {
// Use the useLoaderData hook to get the Page data from `loader` above.
const { page } = useLoaderData<typeof loader>();
// Render the page content from Builder.io
return <Content model="page" apiKey={apiKey} content={page as any} />;
}
Want the latest and greatest of Remix with Builder? We recommend using Gen 2.
Inside the routes directory, create a file named $.tsx. and paste in the code below, making sure to replace YOUR_PUBLIC_API_KEY with your Public API Key, which you can find in the Account section of Builder or by pressing Cmd/Ctrl+k and filtering for "API".
// routes/$.tsx
import { BuilderComponent, builder } from "@builder.io/react";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
// add your Public API Key here
builder.init(YOUR_API_KEY);
// Fetch contents of the page
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
// Fetch data content from Builder.io based on the URL path
const page = await builder
.get("page", {
userAttributes: {
urlPath: `/${params["*"]}`,
},
})
.toPromise();
// Verify the user is previewing or editing in Builder
const isPreviewing = new URL(request.url).searchParams.has("builder.preview");
// If the page is not found and the user is not previewing, throw a 404.
// The CatchBoundary component will catch the error
if (!page && !isPreviewing) {
throw new Response("Page Not Found", {
status: 404,
statusText:
"We couldn't find this page, please check your url path and if the page is published on Builder.io.",
});
}
return { page };
};
// Define and render the page.
export default function Page() {
// Use the useLoaderData hook to get the Page data from `loader` above.
const { page } = useLoaderData<typeof loader>();
// Render the page content from Builder.io
return <BuilderComponent model="page" content={page} />;
}To enable server-side rendering for the @builder.io/react package in your Vite project, you need to update your vite.config.ts file. Add the ssr property to the configuration object exported by defineConfig. Inside the ssr object, set the noExternal property to an array containing the "@builder.io/react" package name. This tells Vite not to externalize the @builder.io/react package during the SSR build process.
// vite.config.ts
export default defineConfig({
ssr: {
noExternal: ["@builder.io/react"],
},
// rest of your Vite configuration
});Make sure to place this configuration within the defineConfig function exported from your vite.config.ts file.
Tip: When deploying the React Gen 2 SDK to an edge runtime, you must use the @builder.io/sdk-react/edge import instead of @builder.io/sdk-react. This is necessary to avoid importing code that only works on NodeJS runtimes.
Inside the routes directory, create a file called ($slug)._index.tsx and paste the following code into it. Replace YOUR_API_KEY with your Public API Key, which you can find in the Account section of Builder.
// ($slug)._index.tsx
import {
Content,
fetchOneEntry,
getBuilderSearchParams,
isPreviewing,
} from '@builder.io/sdk-react';
import type { LoaderFunctionArgs } from '@remix-run/node';
import { useLoaderData } from '@remix-run/react';
import { fetch as webFetch } from '@remix-run/web-fetch';
const apiKey = /* Add your Public API Key here */;
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
const url = new URL(request.url);
const urlPath = `/${params['slug'] || ''}`;
const page = await fetchOneEntry({
model: 'page',
apiKey: apiKey,
options: getBuilderSearchParams(url.searchParams),
userAttributes: { urlPath },
fetch: webFetch,
});
if (!page && !isPreviewing(url.search)) {
throw new Response('Page Not Found', {
status: 404,
statusText: 'Page not found in Builder.io',
});
}
return { page };
};
// Define and render the page.
export default function Page() {
// Use the useLoaderData hook to get the Page data from `loader` above.
const { page } = useLoaderData<typeof loader>();
// Render the page content from Builder.io
return <Content model="page" apiKey={apiKey} content={page as any} />;
}Want the latest and greatest of Hydrogen with Builder? We recommend using Gen 2.
Inside the routes directory, create a file named $.tsx. and paste in the code below, making sure to replace YOUR_PUBLIC_API_KEY with your Public API Key, which you can find in the Account section of Builder or by pressing Cmd/Ctrl+k and filtering for "API".
// routes/$.tsx
import { BuilderComponent, builder } from "@builder.io/react";
import type { LoaderFunctionArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
// add your Public API Key here
builder.init(YOUR_API_KEY);
// Fetch contents of the page
export const loader = async ({ params, request }: LoaderFunctionArgs) => {
// Fetch data content from Builder.io based on the URL path
const page = await builder
.get("page", {
userAttributes: {
urlPath: `/${params["*"]}`,
},
})
.toPromise();
// Verify the user is previewing or editing in Builder
const isPreviewing = new URL(request.url).searchParams.has("builder.preview");
// If the page is not found and the user is not previewing, throw a 404.
// The CatchBoundary component will catch the error
if (!page && !isPreviewing) {
throw new Response("Page Not Found", {
status: 404,
statusText:
"We couldn't find this page, please check your url path and if the page is published on Builder.io.",
});
}
return { page };
};
// Define and render the page.
export default function Page() {
// Use the useLoaderData hook to get the Page data from `loader` above.
const { page } = useLoaderData<typeof loader>();
// Render the page content from Builder.io
return <BuilderComponent model="page" content={page} />;
}To enable server-side rendering for the @builder.io/react package in your Vite project, you need to update your vite.config.ts file. Add the ssr property to the configuration object exported by defineConfig. Inside the ssr object, set the noExternal property to an array containing the "@builder.io/react" package name. This tells Vite not to externalize the @builder.io/react package during the SSR build process.
// vite.config.ts
export default defineConfig({
ssr: {
noExternal: ["@builder.io/react"],
},
// rest of your Vite configuration
});Make sure to place this configuration within the defineConfig function exported from your vite.config.ts file.
This example uses SvelteKit.
<!-- src/routes/+page.svelte -->
<script>
import { Content, isPreviewing } from '@builder.io/sdk-svelte';
// Add your Public API Key
const BUILDER_PUBLIC_API_KEY = YOUR_API_KEY;
export let data;
</script>
<main>
<h1>
Welcome to your Builder-integrated Svelte app
</h1>
<div>Below is your Builder Content:</div>
{#if data.content || isPreviewing()}
<!-- Render builder content with all required props -->
<div>
<div>{data.content?.name}</div>
<Content
model="page"
content={data.content}
apiKey={BUILDER_PUBLIC_API_KEY}
/>
</div>
{:else}
<p>Loading...</p>
{/if}
</main>// src/routes/+page.server.js
import { fetchOneEntry } from '@builder.io/sdk-svelte';
export async function load({ params }) {
if (!params.page) {
params.page = ''; // Set a default value if page is not provided
}
const urlPath = `/${params.page}`;
console.log('Params and urlPath: ', params, urlPath);
// fetch your Builder content
const content = await fetchOneEntry({
model: 'page',
apiKey: YOUR_API_KEY,
userAttributes: {
urlPath,
},
});
console.log('Content: ', content);
// return content from `fetchOneEntry()`
return {
content,
};
}<script>
import { fetchOneEntry, Content, isPreviewing } from '@builder.io/sdk-vue';
export default {
data: () => ({
canShowContent: false,
content: null,
apiKey: BUILDER_PUBLIC_API_KEY,
}),
mounted() {
fetchOneEntry({
model: 'page',
apiKey: YOUR_PUBLIC_API_KEY, // <-- Replace with your Public API Key
userAttributes: {
urlPath: window.location.pathname,
},
}).then(res => {
this.content = res;
this.canShowContent = this.content || isPreviewing();
});
},
}
</script><template>
<Content
v-if="canShowContent"
model="page"
:content="content"
:api-key="apiKey"
/>
</template>If your app doesn't have it, create pages/[...app].vue and paste in the following code.
<!-- pages/[...app].vue -->
<script setup>
import { Content, fetchOneEntry, isPreviewing } from '@builder.io/sdk-vue';
import { ref } from 'vue';
const route = useRoute();
// TO DO: Add your Public API Key here
const apiKey = /* your API key here */;
const canShowContent = ref(false);
const model = 'page';
const { data: content } = await useAsyncData('builderData', () =>
fetchOneEntry({
model,
apiKey,
userAttributes: {
urlPath: route.path,
},
})
);
canShowContent.value = content.value ? true : isPreviewing(route.path);
</script>
<template>
<div v-if="canShowContent">
<Content :api-key="apiKey" :model="model" :content="content" />
</div>
<div v-else>Content not Found</div>
</template>If you are using an SSR framework other than Nuxt, you must import the CSS by adding the following to your entry point, before rendering Builder content:
<script>
import '@builder.io/sdk-vue/css';
</script>import { component$ } from "@builder.io/qwik";
import { routeLoader$ } from "@builder.io/qwik-city";
import { fetchOneEntry, Content, getBuilderSearchParams } from "@builder.io/sdk-qwik";
// Define Builder's public API key and content model.
// TO DO: Replace with your Public API Key
export const BUILDER_PUBLIC_API_KEY = YOUR_PUBLIC_API_KEY;
export const BUILDER_MODEL = "page";
// Define a route loader function that loads
// content from Builder based on the URL.
export const useBuilderContent = routeLoader$(async ({ url, error }) => {
// Fetch content for the specified model using the API key.
const builderContent = await fetchOneEntry({
model: BUILDER_MODEL,
apiKey: BUILDER_PUBLIC_API_KEY,
options: getBuilderSearchParams(url.searchParams),
userAttributes: {
urlPath: url.pathname,
},
});
// Return the fetched content.
return builderContent;
});
// Define a component that renders Builder content
// using Qwik's Content component.
export default component$(() => {
// Call the useBuilderContent function to get the content.
const content = useBuilderContent();
// Specify the content model, pass the fetched content,
// and provide the Public API Key
return (
<Content
model={BUILDER_MODEL}
content={content.value}
apiKey={BUILDER_PUBLIC_API_KEY}
/>
);
});
Paste the following code into App.js.
// App.js
import React, { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
import { Content, fetchOneEntry } from '@builder.io/sdk-react-native';
// TO DO: Add your Public Builder API Key
const BUILDER_API_KEY = YOUR_PUBLIC_API_KEY;
const BuilderContent = () => {
const [content, setContent] = useState(null);
useEffect(() => {
const options = { /* your options here */ };
fetchOneEntry({
model: 'page',
apiKey: BUILDER_API_KEY,
options: options,
userAttributes: {
urlPath: '/',
},
})
.then(setContent)
.catch(err => console.error('Error fetching Builder Content: ', err));
}, []);
return (
<View style={{ padding: 20 }}>
<Text>Hello from your React Native codebase. Below is your Builder content:</Text>
{content ? (
<Content
apiKey={BUILDER_API_KEY}
model="page"
content={content}
/>
) : (
<Text>Not Found.</Text>
)}
</View>
);
};
export default BuilderContent;// landing-page.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { fetchOneEntry, type BuilderContent } from '@builder.io/sdk-angular';
import { Content } from '@builder.io/sdk-angular';
@Component({
selector: 'app-landing-page',
standalone: true,
imports: [Content, CommonModule],
templateUrl: './landing-page.component.html',
styleUrl: './landing-page.component.css'
})
export class LandingPageComponent {
// Add your Public API Key, specify the page model, and initialize the content variable
apiKey = /* ADD YOUR PUBLIC API KEY HERE */;
model = 'page';
content: BuilderContent | null = null;
async ngOnInit() {
const urlPath = window.location.pathname || '';
// fetch the content for the current page based on the current URL
const content = await fetchOneEntry({
apiKey: this.apiKey,
model: this.model,
userAttributes: {
urlPath,
},
});
if (!content) {
return;
}
this.content = content;
}
}<!-- src/app/announcement-bar.component.html -->
<ng-container *ngIf="content">
<builder-content
[model]="model"
[content]="content"
[apiKey]="apiKey">
</builder-content>
</ng-container><!-- app/landing-page.component.html -->
<ng-container *ngIf="content; else notFound">
<builder-content
[model]="model"
[content]="content"
[apiKey]="apiKey"
></builder-content>
</ng-container>
<ng-template #notFound>
<div>404 - Content not found</div>
</ng-template>// app.routes.ts
import { Routes } from '@angular/router';
import { LandingPageComponent } from './landing-page/landing-page.component';
export const routes: Routes = [
{
path: '**',
component: LandingPageComponent,
},
];// app/landing-page/landing-page.component.ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Content, type BuilderContent } from '@builder.io/sdk-angular';
@Component({
selector: 'app-landing-page',
standalone: true,
imports: [Content, CommonModule],
templateUrl: './landing-page.component.html',
styleUrl: './landing-page.component.css'
})
export class LandingPageComponent {
apiKey = /* PUT YOUR PUBLIC API KEY HERE*/;
model = 'page';
content: BuilderContent | null = null;
constructor(private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.activatedRoute.data.subscribe((data: any) => {
this.content = data.content;
});
}
}// app/landing-page/landing-page.resolver.ts
import type { ActivatedRouteSnapshot, ResolveFn } from '@angular/router';
import { fetchOneEntry, getBuilderSearchParams } from '@builder.io/sdk-angular';
export const landingPageResolver: ResolveFn<any> = (
route: ActivatedRouteSnapshot
) => {
const urlPath = `/${route.url.join('/')}`;
const searchParams = getBuilderSearchParams(route.queryParams);
return fetchOneEntry({
apiKey: /* PUT YOUR PUBLIC API KEY HERE */,
model: 'page',
userAttributes: {
urlPath,
},
options: searchParams,
});
};// gatsby-config.js
const path = require('path');
module.exports = {
siteMetadata: {
title: `My Gatsby Site`,
siteUrl: `https://www.yourdomain.tld`,
},
plugins: [
{
resolve: '@builder.io/gatsby',
options: {
// Replace with your Public API Key
publicAPIKey: YOUR_API_KEY,
templates: {
// Render every `page` model as a new page using the
// src/templates/page.jsx template based on the URL provided in Builder.io
page: path.resolve('src/templates/page.jsx'),
},
},
},
],
};In a new subdirectory called src/templates, create a file named page.jsx, and paste in the following:
// src/templates/page.jsx
import * as React from 'react';
import { graphql } from 'gatsby';
import { builder, BuilderComponent } from '@builder.io/react';
// Initialize the Builder SDK with your organization's API Key
// Find the API Key on: https://builder.io/account/settings
builder.init(your_api_key)
// Your template populates your Gatsby pages with your Builder page content.
function PageTemplate({ data }) {
const models = data?.allBuilderModels;
const page = models.page?.content;
return <BuilderComponent name="page" content={page} />;
}
export default PageTemplate;
// pageQuery is a GraphQL query that
// fetches each page's content from Builder.
// Your content is rendered within the
// PageTemplate using BuilderComponent, provided by Builder's SDK.
export const pageQuery = graphql`
query ($path: String!) {
allBuilderModels {
page(target: { urlPath: $path }) {
content
}
}
}
`;const express = require('express');
const app = express();
// Replace with your Public API Key.
const apiKey = YOUR_API_KEY;
const port = 3000;
const handleError = err => {
// The requested Builder content could not be found.
if (err.response.status === 404) {
return { data: null };
}
throw err;
};
// Catchall route
app.get('*', async (req, res) => {
const encodedUrl = encodeURIComponent(req.url);
const { data: pageData } =
await fetch(`https://cdn.builder.io/api/v1/html/page?apiKey=${apiKey}&url=${encodedUrl}`)
.then((res) => res.json())
.catch(handleError);
if (pageData) {
const pageHtml = pageData.html;
res.send(`
<html>
<head> <!-- Your head content here --> </head>
<body>
${pageHtml}
</body>
</html>
`);
} else {
res.status(404);
res.send(/* Your 404 page HTML */);
}
});
app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Add the following code to the view file you want to use.
This blueprint assumes you are familiar with the tech stack and practices for integrating Builder. For more detailed instructions that use this same code, show how to install the Builder Swift SDK, and set up Appetize.io with Builder, visit Integrating Pages.
// add to a view file, such as ContentView.swift
// default Swift import
import SwiftUI
// Import BuilderIO wherever you need to use the SDK methods
import BuilderIO
struct ContentView: View {
@ObservedObject var content: BuilderContentWrapper = BuilderContentWrapper()
init() {
self.getContent()
}
// Define getContent as a method to fetch content from Builder.io
func getContent() {
Content.getContent(model: "page",
apiKey: YOUR_PUBLIC_API_KEY, // TO DO: Replace with Public API key
url: "/test-page", // TO DO: Create a Page in Builder called test-page
locale: "",
preview: "") { content in
// The completion block to be executed once getContent is completed
// Ideally in the main thread because it likely updates UI components
DispatchQueue.main.async {
// Calls changeContent on self.content with the new content
self.content.changeContent(content)
}
}
}
var body: some View {
VStack {
if let contentValue = content.content {
// Use the content to render your view
// TO DO: Replace with Public API key
RenderContent(content: contentValue, apiKey: YOUR_PUBLIC_API_KEY)
} else {
// Display a loading message if the content is not yet available
Text("Loading...")
}
// Handle live previewing
if Content.isPreviewing() {
// Display a 'Reload' button during content previews for manual refresh
Button("Reload") {
self.getContent()
}
// Listen for shake gesture notification to refresh content
.onReceive(NotificationCenter.default.publisher(for: deviceDidShakeNotification)) { _ in
self.getContent()
}
}
}
}
}
// Make sure `deviceDidShakeNotification` is defined as a Notification.Name
// This could be in the same file or in another part of your code
let deviceDidShakeNotification = Notification.Name("DeviceDidShakeNotification")
In app-routing.module.ts, import LandingPageComponent at the top with the other JavaScript imports and replace the default routes with an array with a single item:
import { LandingPageComponent } from './landing-page/landing-page.component';
...
// replace default routes with one wildcard path that goes to LandingPageComponent
[
{
path: '**',
component: LandingPageComponent,
},
]Add the following to landing-page.component.html:
<builder-component
*ngIf="!noBuilderPageForUrl"
model="page"
(load)="noBuilderPageForUrl = $event ? false : true"
(error)="noBuilderPageForUrl = true"
>
<!-- Default content inside the tag shows while the builder content is fetching -->
<div class="spinner"></div>
</builder-component>
<!-- add your page not found component here -->
<app-page-not-found *ngIf="noBuilderPageForUrl"> </app-page-not-found>In landing-page.component.ts, add the following property:
noBuilderPageForUrl: boolean = false;Replace the contents of app.component.html with:
<div class="main">
<h2>This section loads Builder pages unless page not found, in which case it loads the PageNotFoundComponent from the Angular app.</h2>
<router-outlet></router-outlet>
</div>For more detailed instructions, visit Integrating Pages.
© 2020 Builder.io, Inc.