A contact page for a studio that answers before you write: masthead navbar, a contact panel with the address and reply time, a FAQ with a booking aside, three rated accounts of the first call, and a mega footer.
Composed from 5 blocks. Installing this page installs all of them, plus the pieces and primitives they use.
Built in the Polaris collection.
A complete contact page for a studio that answers before you write: a masthead navbar carrying the location and inbox, a contact panel on a tinted surface instead of a hero, an FAQ with a booking card beside it, an inverted band of client quotes about the first conversation, and a four-column footer. Five blocks, one install.
Upgrade to Pro
Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.
Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.
npx shadcn add "https://ui.beste.co/page/r/contact1?email=YOUR_EMAIL&license_key=YOUR_KEY"This installs the page to components/beste/page/contact1.tsx and everything behind it in one command: the five blocks it composes (navbar52, cta68, faq69, testimonial30, footer102), the button1 and badge6 components they use, and the shadcn/ui button primitive. Eight files, no follow-up installs.
The installed file exports contact1Demo alongside the page: the exact props behind the preview above. Spread it to get a working page in one line.
import { Contact1, contact1Demo } from "@/components/beste/page/contact1";
export default function Page() {
return <Contact1 {...contact1Demo} />;
}Each section is a separate prop, so the usual next step is to keep the demo and replace one section at a time rather than writing all five at once.
import { Contact1, contact1Demo } from "@/components/beste/page/contact1";
export default function Page() {
return (
<Contact1
{...contact1Demo}
navbar={{
brand: "Halden",
tagline: "Design and build, two projects at a time",
locationLabel: "Oslo, 09:40",
email: "studio@halden.example",
links: [
{ label: "Work", href: "/work" },
{ label: "Studio", href: "/studio" },
],
cta: { label: "Start a project", href: "/contact" },
}}
contact={{
label: "Contact",
badge: { label: "Booking from March" },
heading: "Tell us what is actually wrong.",
description: "Not the deliverable, the problem. One paragraph is plenty.",
button: { label: "Write to the studio", href: "mailto:studio@halden.example" },
secondaryLink: { label: "Or book a call", href: "/call" },
meta: ["studio@halden.example", "Oslo, Norway", "Reply within two days"],
}}
/>
);
}Every prop is the full prop object of the block that renders that section, so anything the block accepts is accepted here.
| Prop | Type | Default | Description |
|---|---|---|---|
navbar | ComponentProps<typeof Navbar52> | – | Masthead: brand, tagline, location, inbox, links, CTA |
contact | ComponentProps<typeof Cta68> | – | The contact panel, standing in for a hero |
faq | ComponentProps<typeof Faq69> | – | Questions, plus the side card that offers a call |
testimonial | ComponentProps<typeof Testimonial30> | – | Client quotes, rendered on the inverted band |
footer | ComponentProps<typeof Footer102> | – | Brand blurb, socials, link columns, legal row |
className | string | – | Extra classes for the page's outer wrapper |
| Section | Block | Notes |
|---|---|---|
| Masthead | navbar52 | Carries a tagline, local time and email beside the links |
| Contact | cta68 | Given bg-muted/50 by the page, so it reads as the opening surface |
| Questions | faq69 | Two columns: the questions, and an image card offering a call |
| Quotes | testimonial30 | Six quotes; the demo fills the grid rather than leaving a gap |
| Footer | footer102 | Brand column, three link columns, socials and a legal row |
faq removes that section entirely rather than rendering an empty one. A page with only navbar and contact is valid.className="bg-muted/50" on the contact block after spreading contact, so a className passed inside that prop is overridden. Tint that section from the page, not through the prop.dark class, which swaps the theme tokens for that subtree. The cards, borders and muted labels inside the block follow the switch, which they would not do if the section were merely painted a dark colour.dark rather than a fixed colour, it inverts against whatever theme is active: on a dark site it is the light band that would need adding, not the dark one removing.button.href at a mailto: link or at your own form route.