Integrate Announcement Bars
An announcement bar at the top of your site is a common pattern in Builder.
Model definition
A standard section model named announcement-bar is all you need. By default, you don't need any fields. Instead, use targeting to decide which announcement bar shows where.
Example Code
// pages/[...page].tsx
import React from "react";
import { BuilderComponent, builder } from "@builder.io/react";
import { GetStaticProps } from "next";
// TO DO: Add your Public API Key
builder.init('YOUR_API_KEY');
export const getStaticProps: GetStaticProps = async ({ params }) => {
const urlPath = '/' + (Array.isArray(params?.page) ? params.page.join('/') : params?.page || '');
// Fetch the content for the specific section using its model name
const announce = await builder
.get("announcement-bar", {
userAttributes: {
urlPath,
},
})
.toPromise();
return {
props: {
announce: announce || null,
},
};
};
export default function Page({ announce }) {
return (
<>
{announce && (
<BuilderComponent
model="announcement-bar"
content={announce}
/>
)}
<RestOfYourPage />
</>
);
}
import {
Content,
fetchOneEntry,
type BuilderContent,
} from '@builder.io/sdk-react';
import type { GetStaticPaths, GetStaticProps } from 'next';
const BUILDER_API_KEY = /* Your Public API key */;
const MODEL_NAME = 'announcement-bar';
export const getStaticProps: GetStaticProps = async ({ params }) => {
const urlPath =
'/announcements/' +
(Array.isArray(params?.page) ? params.page.join('/') : params?.page || '');
const announcementBar = await fetchOneEntry({
model: MODEL_NAME,
apiKey: BUILDER_API_KEY,
userAttributes: { urlPath },
});
return {
props: { content: announcementBar },
revalidate: 5
};
};
export default function AnnouncementBarPage(props: { content: BuilderContent | null }) {
return (
<>
{props.content && (
<Content
content={props.content}
model={MODEL_NAME}
apiKey={BUILDER_API_KEY}
/>
)}
{/* content coming from your app (or also Builder) */}
<RestOfYourPage />
</>
);
}// 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);
// Define the expected shape of the props
// object passed to the Page function
interface PageProps {
params: {
page: string[];
};
}
// Async function called Page takes a single
// argument called props of type PageProps
export default async function Page(props: PageProps) {
const builderModelName = "announcement-bar";
const announcementBarContent = await builder
// Get the page content from Builder with the specified options
.get(builderModelName, {
userAttributes: {
// Use the page path specified in the URL to fetch the content
urlPath: "/" + (props?.params?.page?.join("/") || ""),
},
// Set prerender to false to prevent infinite rendering loops
prerender: false,
})
// Convert the result to a promise
.toPromise();
return (
<>
{
announcementBarContent &&
<RenderBuilderContent
model={builderModelName}
content={announcementBarContent}
/>
}
<RestOfYourPage/>
</>
);
}Notice that RenderBuilderContent is a component you'd make. In this example, RenderBuilderContent is in builder.tsx:
"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;
}//src/app/[[...slug]]/page.tsx
import {
Content,
fetchOneEntry,
getBuilderSearchParams,
isEditing,
isPreviewing,
} from '@builder.io/sdk-react';
interface PageProps {
params: {
slug: string[];
};
searchParams: Record<string, string>;
}
const apiKey = /* Your Public API key */;
const model = 'announcement-bar';
export default async function Page(props: PageProps) {
const urlPath = '/announcements/' + (props.params?.slug?.join('/') || '');
const announcementBar = await fetchOneEntry({
apiKey,
model,
options: getBuilderSearchParams(props.searchParams),
userAttributes: { urlPath },
});
const canShowAnnouncementBar =
announcementBar ||
isPreviewing(props.searchParams) ||
isEditing(props.searchParams);
return (
<>
{canShowAnnouncementBar && (
<Content content={announcementBar} apiKey={apiKey} model={model} />
)}
{/* Your content coming from your app (or also Builder) */}
<RestOfYourPage/>
</>
);
}import { useEffect, useState } from "react";
import { BuilderComponent, builder } from "@builder.io/react";
// Replace with your Public API Key.
builder.init(YOUR_API_KEY);
export default function Page() {
const [announcement, setAnnouncement] = useState(null);
useEffect(() => {
builder
.get("announcement-bar", {
userAttributes: {
// To allow targeting different announcements at different pages (URLs)
urlPath: window.location.pathname,
},
})
.toPromise()
.then((announcementBar) => setAnnouncement(announcementBar));
}, []);
return (
<>
<BuilderComponent model="announcement-bar" content={announcement} />
{/* Put the rest of your page here. */}
<TheRestOfYourPage />
</>
);
}
/**
* src/components/AnnouncementBar.tsx
*/
import {
Content,
fetchOneEntry,
getBuilderSearchParams,
type BuilderContent,
} from '@builder.io/sdk-react';
import { useEffect, useState } from 'react';
const BUILDER_API_KEY = /* Your Public API Key */;
const MODEL_NAME = 'announcement-bar';
export default function AnnouncementBar() {
const [content, setContent] = useState<BuilderContent | null>(null);
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);
}
})
.catch((err) => {
console.log('Oops: ', err);
});
}, []);
return (
<>
{content && (
<Content
content={content}
model={MODEL_NAME}
apiKey={BUILDER_API_KEY}
/>
)}
{/* content coming from your app (or also Builder) */}
<div>The rest of your page goes here</div>
</>
);
}// $.tsx
import {
fetchOneEntry,
isEditing,
isPreviewing,
Content,
getBuilderSearchParams
} from "@builder.io/sdk-react";
import type { LoaderFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
export const loader: LoaderFunction = async ({ params, request }) => {
const url = new URL(request.url);
const apiKey = "YOUR_API_KEY"; // Replace with your actual API key
const content = await fetchOneEntry({
model: "announcement-bar",
apiKey: apiKey,
options: getBuilderSearchParams(new URL(request.url).searchParams),
userAttributes: {
urlPath: `/${params["*"]}`,
// add other targeting options
},
});
const isEditingOrPreviewing = isEditing() || isPreviewing();
if (!content && !isEditingOrPreviewing) {
// if section doesn't exist, return an empty object
return { content: null };
}
return { content };
};
// Define a section –– Announcement Bar.
export default function Page() {
// Use the useLoaderData hook to get the announcement-bar data from `loader` above.
const { content } = useLoaderData<typeof loader>();
// Render the announcement-bar content from Builder.io
return (
<>
<Content
model="announcement-bar"
apiKey="YOUR_API_KEY"
content={content}
/>
<RestOfYourPages/>
</>
);
}Want the latest and greatest of Remix with Builder? We recommend using Gen 2.
Paste the following code into a new file within the routes directory called $.tsx, making sure to replace YOUR_API_KEY with your Public API Key.
// $.tsx
import { BuilderComponent, builder } from "@builder.io/react";
import type { LoaderArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
// Initialize the Builder client and pass in your Public API Key
builder.init(YOUR_PUBLIC_API_KEY); // <-- add your Public API Key here
// Fetch contents of the announcement bar section
export const loader = async ({ params, request }: LoaderArgs) => {
// Fetch data content from Builder.io based on the URL path
const announcementBar = await builder
.get("announcement-bar", {
userAttributes: {
urlPath: `/${params["*"]}`,
},
})
.toPromise();
// Verify if the user is previewing or editing in Builder
const isPreviewing = new URL(request.url).searchParams.has("builder.preview");
// If the announcement bar is not found and the user is not previewing, return null
if (!announcementBar && !isPreviewing) {
return null;
}
return { announcementBar };
};
// Define and render the page.
export default function Page() {
// Use the useLoaderData hook to get the announcement bar data from `loader` above.
const { announcementBar } = useLoaderData<typeof loader>();
// Render the announcement bar content from Builder.io
return (
<>
{announcementBar && (
<BuilderComponent model="announcement-bar" content={announcementBar} />
)}
<RestOfYourPage/>
</>
)
}In your project, be sure that package.json has "type": "commonjs".
In remix.config.cjs (update the file extension to .cjs if needed), and update two parts:
- Change
export defaulttomodule.exports. - Add
serverModuleFormat: "cjs".
// $.tsx
import {
fetchOneEntry,
isEditing,
isPreviewing,
Content,
getBuilderSearchParams
} from "@builder.io/sdk-react";
import type { LoaderFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
export const loader: LoaderFunction = async ({ params, request }) => {
const url = new URL(request.url);
const apiKey = "YOUR_API_KEY"; // Replace with your actual API key
const content = await fetchOneEntry({
model: "announcement-bar",
apiKey: apiKey,
options: getBuilderSearchParams(event.url.searchParams),
userAttributes: {
urlPath: `/${params["*"]}`,
// add other targeting options
},
});
const isEditingOrPreviewing = isEditing() || isPreviewing();
if (!content && !isEditingOrPreviewing) {
// if section doesn't exist, return an empty object
return { content: null };
}
return { content };
};
// Define a page.
export default function Page() {
// Use the useLoaderData hook to get the announcement-bar data from `loader` above.
const { content } = useLoaderData<typeof loader>();
// Render the announcement-bar content from Builder.io
return (
<Content
model="announcement-bar"
apiKey="YOUR_API_KEY"
content={content}
/>
);
}In your project, be sure that package.json has "type": "commonjs".
In remix.config.cjs (update the file extension to .cjs if needed), and update two parts:
- Change
export defaulttomodule.exports. - Add
serverModuleFormat: "cjs".
Want the latest and greatest of Remix with Builder? We recommend using Gen 2.
// $.tsx
import { BuilderComponent, builder } from "@builder.io/react";
import type { LoaderArgs } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
// TO DO: Add your Public API Key
builder.init(YOUR_PUBLIC_API_KEY);
// Fetch contents of the announcement bar section
export const loader = async ({ params, request }: LoaderArgs) => {
// Fetch data content from Builder.io based on the URL path
const announcementBar = await builder
.get("announcement-bar", {
userAttributes: {
urlPath: `/${params["*"]}`,
},
})
.toPromise();
// Verify if the user is previewing or editing in Builder
const isPreviewing = new URL(request.url).searchParams.has("builder.preview");
// If the announcement bar is not found and the user is not previewing, return null
if (!announcementBar && !isPreviewing) {
return null;
}
return { announcementBar };
};
// Define and render the page.
export default function Page() {
// Use the useLoaderData hook to get the announcement bar data from `loader` above.
const { announcementBar } = useLoaderData<typeof loader>();
// Render the announcement bar content from Builder.io
return (
<>
{announcementBar && (
<BuilderComponent model="announcement-bar" content={announcementBar} />
)}
<
</>
);
}In your project, be sure that package.json has "type": "commonjs".
In remix.config.cjs (update the file extension to .cjs if needed), and update two parts:
- Change
export defaulttomodule.exports. - Add
serverModuleFormat: "cjs".
+page.server.js, define load() function that fetches the content for the announcement-bar section:/* src/routes/homepage/+page.server.js */
import { fetchOneEntry, getBuilderSearchParams } from '@builder.io/sdk-svelte';
/** @type {import('../../$types').PageServerLoad} */
export async function load(event) {
// fetch your Builder content
const content = await fetchOneEntry({
model: 'announcement-bar',
apiKey: /* Add your Public API Key */,
options: getBuilderSearchParams(event.url.searchParams),
userAttributes: {
urlPath: event.url.pathname || '/',
},
});
return { content };
}+page.svelte page.<!-- src/routes/homepage/+page.svelte -->
<script>
import { isPreviewing, Content } from '@builder.io/sdk-svelte';
const apiKey = /*Your Public API key*/;
const model = 'announcement-bar';
export let data;
const canShowContent = data.content || isPreviewing();
</script>
<main>
{#if canShowContent}
<!-- Your Announcement Bar section -->
<Content {model} content={data.content} {apiKey} />
{/if}
<OtherRestOfYourPage/>
</main>
builder.get('your-model', {
query: {
'data.myCustomProperty.$eq': 'sale'
}
})Create a page with the following contents. Make sure to replace YOUR_API_KEY with your Public API Key:
// src/views/your-page.vue
<template>
<Content
v-if="canShowContent"
:model="model"
:content="content"
:api-key="apiKey"
/>
<RestOfYourPage
</template>
<script setup lang="ts">
import {
Content,
type BuilderContent,
fetchOneEntry,
getBuilderSearchParams,
isPreviewing,
} from '@builder.io/sdk-vue';
import { onMounted, ref } from 'vue';
const content = ref<BuilderContent | null>(null);
const apiKey = /* Your Public API key */;
const canShowContent = ref(false);
const model = 'announcement-bar';
onMounted(async () => {
content.value = await fetchOneEntry({
model,
apiKey,
options: getBuilderSearchParams(new URL(location.href).searchParams),
userAttributes: {
urlPath: window.location.pathname,
},
});
canShowContent.value = content.value ? true : isPreviewing();
});
</script>Import fetchOneEntry, Content, and isPreviewing from the Vue SDK.
Using fetchOneEntry(), specify the announcement-bar model, your Public API Key, and the announcement-bar content.
// pages/your-page.vue
<script setup>
import {
Content,
fetchOneEntry,
isPreviewing,
getBuilderSearchParams,
} from '@builder.io/sdk-vue';
import { ref } from 'vue';
const route = useRoute();
const model = 'announcement-bar';
const apiKey = 'ee9f13b4981e489a9a1209887695ef2b';
const canShowAnnouncementBar = ref(false);
const { data: announcement } = await useAsyncData('builderData', () =>
fetchOneEntry({
model,
apiKey,
options: getBuilderSearchParams(route.query),
userAttributes: { urlPath: route.path },
})
);
canShowAnnouncementBar.value = announcement.value ? true : isPreviewing(route.query);
</script>
<template>
<Content
v-if="canShowAnnouncementBar"
:model="model"
:content="announcement"
:api-key="apiKey"
/>
<RestOfYourPage/>
</template>Paste the following code into the appropriate .tsx file within the src/pages directory:
import { component$, Resource, useResource$ } from "@builder.io/qwik";
import { useLocation } from "@builder.io/qwik-city";
import { fetchOneEntry, Content, getBuilderSearchParams } from "@builder.io/sdk-qwik";
export const BUILDER_PUBLIC_API_KEY = YOUR_API_KEY; // <-- Add your Public API KEY here
export const BUILDER_MODEL = "announcement-bar"; // <-- Add your section name here
export default component$(() => {
const location = useLocation();
const builderContentRsrc = useResource$<any>(() => {
return fetchOneEntry({
model: BUILDER_MODEL,
apiKey: BUILDER_PUBLIC_API_KEY,
options: getBuilderSearchParams(url.searchParams),
userAttributes: {
urlPath: location.pathname || "/", // <-- Use for targeting by URL
},
});
});
return (
<Resource
value={builderContentRsrc}
onPending={() => <div>Loading...</div>}
onResolved={(content) => (
<Content
model={BUILDER_MODEL}
content={content}
apiKey={BUILDER_PUBLIC_API_KEY}
/>
)}
/>
);
});The following App.js code is an example of integrating an announcement-bar model. Make sure to replace YOUR_API_KEY with your Public API Key:
// App.js
import type { BuilderContent } from '@builder.io/sdk-react-native';
import { Content, fetchOneEntry } from '@builder.io/sdk-react-native';
import { useLocalSearchParams } from 'expo-router';
import { useEffect, useState } from 'react';
import { Text, View } from 'react-native';
const BUILDER_API_KEY = /* Your Public API key */;
const MODEL_NAME = 'announcement-bar';
export default function AnnouncementScreen() {
const [content, setContent] = useState<BuilderContent | null>(null);
const { slug } = useLocalSearchParams<{ slug: string }>();
useEffect(() => {
fetchOneEntry({
model: MODEL_NAME,
apiKey: BUILDER_API_KEY,
userAttributes: {
urlPath: `/announcements/${slug}`,
},
})
.then((data: BuilderContent) => {
setContent(data);
})
.catch((err) => console.error('Error fetching Builder Content: ', err));
}, []);
return (
<View>
{content && (
<Content
apiKey={BUILDER_API_KEY}
model={MODEL_NAME}
content={content}
/>
)}
<RestOfYourPage/>
</View>
);
}// src/app/announcement-bar.component.ts
import { CommonModule } from '@angular/common';
import { Component } from '@angular/core';
import {
Content,
fetchOneEntry,
type BuilderContent,
} from '@builder.io/sdk-angular';
@Component({
selector: 'app-announcement-bar',
standalone: true,
imports: [CommonModule, Content],
templateUrl: './announcement-bar.component.html',
})
export class AnnouncementBarComponent {
apiKey = /* PUT YOUR PUBLIC API KEY HERE */;
model = 'announcement-bar';
content: BuilderContent | null = null;
async ngOnInit() {
const urlPath = window.location.pathname || '';
const content = await fetchOneEntry({
apiKey: this.apiKey,
model: this.model,
userAttributes: {
urlPath,
},
});
if (!content) {
return;
}
this.content = content;
}
}Fetch the data with a resolver:
import type { ActivatedRouteSnapshot, ResolveFn } from '@angular/router';
import { fetchOneEntry, getBuilderSearchParams } from '@builder.io/sdk-angular';
export const announcementBarResolver: ResolveFn<any> = (
route: ActivatedRouteSnapshot
) => {
const urlPath = `/${route.url.join('/')}`;
const searchParams = getBuilderSearchParams(route.queryParams);
return fetchOneEntry({
apiKey: /* YOUR PUBLIC API KEY */,
model: 'announcement-bar',
userAttributes: {
urlPath,
},
options: searchParams,
});
};
In announcement-bar.component.ts, subscribe to the route's resolved data from the resolver, in order to render it in the template.
// src/app/announcement-bar/announcement-bar.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { Content, type BuilderContent } from '@builder.io/sdk-angular';
@Component({
selector: 'app-announcement-bar',
standalone: true,
templateUrl: './announcement-bar.component.html',
})
export class AnnouncementBarComponent {
apiKey = '/* PUT YOUR PUBLIC API KEY HERE */';
model = 'announcement-bar';
content: BuilderContent | null = null;
constructor(private activatedRoute: ActivatedRoute) {}
ngOnInit() {
this.activatedRoute.data.subscribe((data: any) => {
this.content = data.content;
});
}
}// gatsby-config.js
module.exports = {
...,
plugins: [
{
resolve: '@builder.io/gatsby',
options: {
// Replace with your Public API Key.
publicAPIKey: YOUR_API_KEY,
},
},
],
};Create a page with the following contents:
// src/pages/your-page.jsx
import * as React from 'react';
import { graphql } from 'gatsby';
import { BuilderComponent } from '@builder.io/react';
function Page({ data }) {
const models = data?.allBuilderModels;
const announce = models.oneAnnouncementBar?.content;
return (
<>
<BuilderComponent model="announcement-bar" content={announce} />
{/* Put the rest of your page here. */}
<TheRestOfYourPage />
</>
);
}
export default Page;
export const announceQuery = graphql`
query ($path: String!) {
allBuilderModels {
oneAnnouncementBar(target: { urlPath: $path }) {
content
}
}
}
`;builder.get('your-model', {
query: {
'data.myCustomProperty.$eq': 'sale'
}
})// your-app.js
app.get('/', async (req, res) => {
const apiKey = YOUR_API_KEY;
const encodedUrl = encodeURIComponent(req.url);
// You can also query, sort, and target this content.
// See full docs on our content API: https://www.builder.io/c/docs/query-api
const { data: announceData } =
await fetch(`https://cdn.builder.io/api/v1/html/announcement-bar?apiKey=${apiKey}&url=${encodedUrl}`)
.then((res) => res.json());
const announceHtml = announceData.html;
res.send(`
<html>
<body>
<!-- Put your header here. -->
<nav>...</nav>
${announceHtml}
<!-- Put the rest of your page here. -->
<div>...</div>
</body>
</html>
`);
}
});Tip: When using the HTML API to serve content you need to integrate Builder previewing into your site so that previews are accurate. For detailed instructions, visit Previewing content on your site in the HTML API document.
// Import necessary libraries
import SwiftUI
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: "announcement-bar",
apiKey: YOUR_PUBLIC_API_KEY, // Replace with your Public API key
url: "", // No URL is needed for sections
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 section view
// TO DO: Replace with code to render your section
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()
}
}
}
}
}
For more flexibilty when working with an integrated Swift app, we recommend using Appetize.io. Find detailed instructions in the section Using your app with Builder in Integrating Pages.
Add the following code in any .liquid file that you would like this Builder section to appear on.
{% render 'model.announcement-bar.builder' %}Learn more about Builder's Shopify code generation options and using Builder.io with Shopify for developers.
ng generate component your_component// src/app/app.module.ts
import { BuilderModule } from '@builder.io/angular'; // <-- import at top
...
@NgModule({
...
// Add your API key to the AppModule imports array.
imports: [
BrowserModule,
AppRoutingModule,
BuilderModule.forRoot(YOUR_API_KEY),
],
});<!-- src/app/your-component/your-component.component.html -->
<builder-component model="announcement-bar"></builder-component>For more detail on creating an Announcement Bar in Builder, refer to Integrating Sections.