Make it yours
Where to change the name, the text, the images, the prices, the colours, the fonts, the sections and the links.
Run the site with yarn dev while you edit: the page reloads each time you save. Every path below is relative to the landing-page-template-2 folder.
| To change | Edit |
|---|---|
| The brand name | name in src/config/site.ts |
| Any text on the page | messages/en.json and messages/ar.json |
| Images and logos | public/ and the constants/ folder of each section |
| Prices and plans | src/features/home/Pricing/constants/plans.ts and src/features/home/ComparisonTable/constants/ |
| Colours | src/styles/globals.css and the Tailwind classes in the components |
| Fonts | src/lib/fonts.ts |
| Which sections appear, and their order | src/app/[locale]/page.tsx |
| Menu, footer and social links | src/layouts/Header/constants/, src/layouts/Footer/constants/footer.ts, src/config/site.ts |
| The site's address for search engines and sharing | NEXT_PUBLIC_SITE_URL |
The two settings
| Variable | Default | What it does |
|---|---|---|
NEXT_PUBLIC_SITE_URL | http://localhost:3030 | The public address of the site. The canonical link, the language alternates and the social sharing cards are built from it. Set it to your domain before you go live. |
NEXT_PUBLIC_API_BASE_URL | empty | The API that receives newsletter sign-ups. Only used by the newsletter section. |
Without Docker, put them in .env.local (copy .env.example). With Docker, pass them as build arguments: the image build does not read .env or .env.local files.
NEXT_PUBLIC_SITE_URL=https://www.your-domain.comlanding-page-template-2docker build -t saas-landing --build-arg NEXT_PUBLIC_SITE_URL=https://www.your-domain.com .Both values are compiled into the page when it is built. After a change, restart yarn dev, run yarn build again, or build the Docker image again. Never put a secret in a NEXT_PUBLIC_ value: every visitor can read it.
The brand name and logo
name in src/config/site.ts is the brand. The logo in the header and the footer is its first letter in a coloured square, followed by the name, drawn by src/components/Logo.tsx. Texts that say {brand} in the message files use it too.
name: "SaasPro",The same value fills the page title, the social sharing card, the Resources menu and the web app manifest, which src/app/manifest.ts builds in the page's dark colours. You change the name in one place.
creator in the same file is the author named in the page's metadata. Change it too.
Text
Every visible word, every image description and the page title live in messages/en.json and messages/ar.json. Each section has its own block, named after it: Header, Hero, SocialProof, Features, HowItWorks, Testimonials, Pricing, Comparison, Integrations, Faq, Blog, Cta, Newsletter and Footer.
- Change the value, keep the key, and change it in both files. A key missing from one file shows as a missing-message error on that language's page.
- Words in curly braces, such as
{brand}and{year}, are filled in by the app. Keep them as they are. - In
HowItWorks.title, the words inside<highlight>and</highlight>are drawn in the gradient colour. Keep both tags.
Images
| Image | File | Set in |
|---|---|---|
| Hero image | public/images/hero.webp | src/features/home/Hero/constants/hero.ts |
| How it works, one per step | public/images/dashboard.png, team.png, webinar.png, automation.png | src/features/home/HowItWorks/constants/steps.ts |
| Header menu pictures | public/images/dashboard.png, webinar.png | src/layouts/Header/constants/mega-menus.ts |
| Company logos | public/images/logos/*.svg | src/features/home/SocialProof/constants/companies.ts |
| Integration logos | public/images/logos/*.svg | src/features/home/Integrations/constants/integrations.ts |
| Testimonial photos | images.unsplash.com | src/features/home/Testimonials/constants/testimonials.ts |
| Blog card images | images.unsplash.com | src/features/home/BlogPreview/constants/articles.ts |
| Favicons | public/favicon.svg, favicon.ico, favicon-96x96.png, apple-touch-icon.png, web-app-manifest-*.png | src/app/[locale]/layout.tsx, src/app/manifest.ts |
| Social sharing image, 1200 by 630 | public/image.png | ogImage in src/config/site.ts |
The easiest change is to replace a file with your own under the same name. For a new file, put it in public/ and use its path from the site's root, such as /images/your-photo.jpg.
An image from another website only loads if its host is listed under images.remotePatterns in next.config.js. Only images.unsplash.com is listed. Add your host there, or copy the files into public/.
remotePatterns: [
{ protocol: "https", hostname: "images.unsplash.com" },
{ protocol: "https", hostname: "cdn.your-domain.com" },
],Prices and plans
The prices are in two places, one per section. Change both so they agree:
| Section | Prices and features | Text |
|---|---|---|
| Pricing cards | src/features/home/Pricing/constants/plans.ts: price, featureIds and popular per plan | Pricing.plans in the message files |
| Comparison table | src/features/home/ComparisonTable/constants/pricing-tiers.ts for the prices, comparison-features.ts for what each plan includes | Comparison.tiers and Comparison.features in the message files |
A plan's features are listed by id in featureIds, and each id needs its text under the plan in both message files. popular: true marks the plan with the Most Popular badge.
Prices are monthly, in US dollars, formatted for the page's language by src/hooks/usePriceFormatter.ts. To show another currency, change its default currency = "USD" to your currency code, such as "EUR".
Colours
- The accents: purple and pink Tailwind classes (
purple-*,pink-*) in the components, such asfrom-purple-600 to-pink-500on the buttons and the logo. Search thesrcfolder forpurple-andpink-to change them all. - The page background:
bg-gray-950on the page's outer element insrc/app/[locale]/page.tsx. The sections usegray-*classes on top of it. - The shared button and input: the CSS variables in
src/styles/globals.css, such as--primaryand--ring. The.darkblock is the one in use.
The page is always dark: src/app/[locale]/layout.tsx sets the dark class and starts the theme provider with defaultTheme="dark". There is no light theme or theme switch.
Fonts
src/lib/fonts.ts loads Inter for English and Noto Sans Arabic for Arabic from Google Fonts, through next/font. The fonts are downloaded when the site is built and then served by the site itself. src/app/[locale]/layout.tsx picks the font by language.
import { Inter, Noto_Sans_Arabic } from "next/font/google";
export const inter = Inter({ subsets: ["latin"] });
export const notoSansArabic = Noto_Sans_Arabic({ subsets: ["arabic"] });To use another Google font, import it from next/font/google in place of one of these and keep the exported name, or change the name where layout.tsx uses it.
Sections
src/app/[locale]/page.tsx lists the sections in the order they appear. Remove a line to hide a section, or move it to change the order. Each section lives in its own folder under src/features/home/.
<Header />
<Hero />
<SocialProof />
<Features />
<HowItWorks />
<Testimonials />
<Pricing />
<ComparisonTable />
<Integrations />
<Faq />
<BlogPreview />
<Cta />
<Footer />The header links to sections by their id, such as #pricing and #testimonials. If you remove a section, remove its link too.
To add a section:
- Create
src/features/home/<Section>/with anindex.tsx, andcomponents/andconstants/folders as the other sections have. - Add its texts under a new key in both message files and read them with
useTranslations. - Export it from
src/features/home/index.tsand place it insrc/app/[locale]/page.tsx.
The newsletter section
A newsletter section with an email form ships in src/features/home/Newsletter/, but it is not on the page. To show it, import it with the others and place it, for example before the call to action:
import {
…
BlogPreview,
Newsletter,
Cta,
} from "@/features/home";
<BlogPreview />
<Newsletter />
<Cta />The form needs an API of your own. Set NEXT_PUBLIC_API_BASE_URL to its address; the form sends { "email": "…" } as JSON in a POST to <NEXT_PUBLIC_API_BASE_URL>/newsletter/subscribe, straight from the visitor's browser.
- A
2xxanswer shows the success message and clears the form. - Any other answer, a network error, or an empty
NEXT_PUBLIC_API_BASE_URLshows the error message. Nothing is sent while the variable is empty. - Because the browser sends the request, your API must allow your site's address in its CORS settings.
The texts are under Newsletter in the message files.
Links and buttons
Most links in the template point to # until you give them a destination:
| Links | Edit |
|---|---|
| Header links (Pricing, Testimonials) | src/layouts/Header/constants/nav-links.ts |
| Products and Resources menus | src/layouts/Header/constants/mega-menus.ts |
| Mobile menu | src/layouts/Header/constants/mobile-menu.ts |
| Footer columns | footerColumns in src/layouts/Footer/constants/footer.ts |
| Social icons in the footer | links in src/config/site.ts |
| Log in in the header | src/layouts/Header/components/HeaderActions.tsx |
| How it works button and step links | src/features/home/HowItWorks/components/CtaButton.tsx and StepDetails.tsx |
| Blog cards | src/features/home/BlogPreview/components/ArticleCard.tsx |
The Get Started, Start Free Trial and Book a Demo buttons, and the buttons on the pricing cards, are plain buttons with no destination. To make one a link, give the Button the asChild prop and put a Link inside it, as src/components/LocaleSwitcher.tsx does:
import { Link } from "@/i18n/navigation";
<Button asChild className="…">
<Link href="/signup">{common("getStarted")}</Link>
</Button>The Link from @/i18n/navigation keeps the visitor's language in the address. For another website, use a plain <a href="https://…"> instead.
Dates and the copyright year
- The footer's copyright year is the year the site was built, from
COPYRIGHT_YEARinsrc/layouts/Footer/constants/footer.ts. Build again in a new year to update it. - Each blog card's date and reading time are
publishedAtandreadMinutesinsrc/features/home/BlogPreview/constants/articles.ts; its title and summary are underBlogin the message files.
Search engines and sharing
- The page title and description are
Metadata.titleandMetadata.descriptionin each message file, so each language has its own. NEXT_PUBLIC_SITE_URLgives every page its canonical address (/enor/ar) and the links between the two languages. Set it to your domain before you build for production, or search engines and social cards point atlocalhost.- The social sharing card uses
public/image.png(1200 by 630) with the same title and description. - The template ships no
sitemap.xmlorrobots.txt.