Template Documentation

01 · Overview

Introduction

Everything you need to know before you open your editor — what’s in the box, what it’s built with, and what it expects from your machine.

Welcome

Thanks for picking up SeoGuru, an Astro template for SEO agencies, PPC/paid-media shops, and other marketing consultancies. This guide walks through installing the template, understanding how the project is organized, customizing content and styling, and shipping it to production. It’s written for developers who are comfortable with the command line but new to this specific codebase — if you already know Astro well, feel free to skip ahead using the sidebar.

What’s Included

Source files

The full, uncompiled project source — components, pages, styles, and configuration.

Production build

A pre-built dist/ folder you can deploy as-is if you don’t plan to customize anything.

Design assets

No separate Figma source file is bundled. Stock photography (Unsplash, Pexels), a 3D illustration pack, and the three bundled font families are credited with license links in CREDITS.md at the project root.

This documentation

A single offline HTML file — open it directly in any browser, no server required.

Features

  • A homepage, about page, three service pages, a contact page, a pricing page, and a privacy policy page — ready to relabel for your own agency.
  • A case-study blog built on Astro content collections, with automatic list, category archive, and tag archive pages generated from Markdown files.
  • No UI framework required — every interactive piece (mobile nav, dropdowns, tabs, accordions, the client-logo marquee) is a small vanilla-JS controller, paired with plain CSS.
  • GSAP-powered scroll animations layered on top of the same vanilla-JS approach.
  • An auto-generated XML sitemap via @astrojs/sitemap, plus per-page SEO meta tags and Open Graph/Twitter card support.

Technology Stack

SeoGuru is plain Astro — no UI framework, and no CSS framework or preprocessor:

Astro 7 Plain CSS Vanilla JavaScript GSAP @astrojs/sitemap

Requirements

ToolMinimum versionNotes
Node.js22.12.0 or laterSet in package.json’s engines field; LTS releases are recommended
Package managernpm (bundled with Node.js)The project ships a package-lock.json; pnpm or Yarn will also work if you prefer
Code editor—VS Code with the official Astro extension is a comfortable default
Git2.xOnly required if you plan to version-control your customized copy

Browser Support

The built site targets the last two versions of all evergreen browsers:

Chrome Firefox Safari Edge
Internet Explorer

Internet Explorer 11 is not supported. No additional polyfills are bundled.

02 · Setup

Getting Started

From a downloaded ZIP to a running dev server in five steps.

Installation & Extraction

  1. Download the template ZIP from your purchase confirmation email or account downloads page.
  2. Extract it to a folder on your machine — avoid folder names with spaces or special characters.
  3. Open that folder in your terminal and in your code editor.
bash
# Extract on macOS / Linux
unzip seoguru-astro.zip -d seoguru-astro
cd seoguru-astro

Node.js Setup

Check which version of Node.js is currently active:

bash
node --version
# v22.12.0 or later

If you manage multiple Node versions, tools like nvm or fnm make switching painless:

bash
nvm install 22
nvm use 22

Install Dependencies

From the project root, install packages with your preferred package manager:

npm
npm install
pnpm
pnpm install
yarn
yarn install

This reads package.json and downloads every dependency into a local node_modules/ folder. It only needs to be re-run when dependencies change.

Run the Development Server

npm
npm run dev
pnpm
pnpm dev

This starts Astro’s local dev server with hot module reloading — edits to components, pages, and styles appear in the browser immediately without a manual refresh. By default it’s available at http://localhost:4321; the terminal output will confirm the exact address. The project’s own CLAUDE.md also documents an astro dev --background mode with astro dev stop / astro dev status / astro dev logs companion commands, if you’re working with an AI coding agent.

Build for Production & Preview

bash
# Compile an optimized production build into dist/
npm run build

# Serve that build locally to sanity-check it
npm run preview

build type-checks the project, bundles and minifies assets, and writes static output to dist/. preview serves that exact output locally so you can verify it before deploying — it does not rebuild automatically, so re-run build after further changes.

Which command do I actually need?

Use dev while you’re customizing the template. Use build, then preview, once before you deploy — it catches issues (like broken TypeScript types or missing imports) that the dev server tolerates but production won’t.

03 · Reference

Project Structure

A tour of the folders you’ll actually touch, and the ones you generally shouldn’t.

PathPurpose
public/Fonts, images, videos, and the favicon — copied to the output root untouched, no optimization or hashing. Referenced from markup with root-relative paths like /images/Logo.svg.
src/components/Small, reusable pieces of UI (Header, Footer, ServicePage, CaseStudyCard, PricingSection, and others) shared across multiple pages.
src/content/case-studies/One Markdown file per case study, validated against the schema in content.config.js. See Dynamic Routes.
src/data/The faqs.js array of question/answer pairs, shared by the homepage and pricing page FAQ sections.
src/layouts/Layout.astro — the single shared page shell: <head> tags, global stylesheet imports, and the site.js/animations.js script tags.
src/pages/Every file here becomes a route automatically. See Pages for details.
src/scripts/site.js (mobile nav, dropdowns, tabs, accordions) and animations.js (GSAP scroll animations) — loaded as plain <script> tags, no bundler-managed imports required.
src/styles/Plain CSS, no preprocessor: normalize.css, framework.css (Webflow-derived base styles), seoguru.css (this template’s design), and custom.css (post-conversion additions). See Styling.
Files not shown above

content.config.js defines the case-studies content collection’s schema (title, images, category, tags, client details, and so on) using Astro’s content layer API. CREDITS.md lists third-party photo, illustration, and font attribution; CHANGELOG.md holds the version history.

04 · Reference

Configuration

The files that control how the project builds, and where to set site-wide values.

package.json

Defines dependencies and the scripts you run from the terminal (dev, build, preview). Add your own script here if you introduce a new tool.

jsonpackage.json
{
  "name": "seoguru-astro",
  "version": "0.0.1",
  "engines": { "node": ">=22.12.0" },
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "astro": "astro"
  }
}

astro.config.mjs

Controls integrations, the deployed site URL, and build behavior. SeoGuru registers a single integration — the official sitemap generator:

javascriptastro.config.mjs
import { defineConfig } from 'astro/config';
import sitemap from '@astrojs/sitemap';

export default defineConfig({
  site: 'https://example.com', // replace with your real deployed URL before launch
  integrations: [sitemap()],
});
Set your real site URL before deploying

The site value still reads the placeholder https://example.com. It feeds the sitemap and the canonical/Open Graph URLs built in Layout.astro, so update it to your real deployed domain before you ship — see Production Checklist.

tsconfig.json

SeoGuru doesn’t use TypeScript as a project-wide choice — there are no .ts/.tsx files, and only one component (PricingSection.astro) declares an optional interface Props. This file just extends Astro’s strict preset, which gives you editor type-checking on .astro frontmatter; there are no path aliases configured:

jsontsconfig.json
{
  "extends": "astro/tsconfigs/strict",
  "include": [".astro/types.d.ts", "**/*"],
  "exclude": ["dist"]
}

Environment Variables (.env)

SeoGuru doesn’t use any environment variables out of the box — there’s no .env file, and nothing in src/ reads import.meta.env. The site URL, form endpoints, and contact details are plain hard-coded values (see Production Checklist for where to change them). If you add your own API integration later, Astro’s standard convention still applies: create a .env file in the project root, and prefix any value that needs to be readable in client-side code with PUBLIC_.

env.env
# Exposed to the browser — never put secrets here
PUBLIC_SITE_URL=https://example.com

# Server-only — safe for secrets, not readable in browser code
API_SECRET_KEY=your_secret_key
Never commit .env

If you introduce one, add .env to .gitignore (it usually is by default for Astro projects). Anything without the PUBLIC_ prefix should stay off the client entirely.

05 · Guide

Pages

How routes, dynamic case-study pages, and the shared layout fit together.

Creating a New Page

Astro uses file-based routing: any .astro file placed in src/pages/ automatically becomes a route matching its file path. SeoGuru’s own routes:

text
src/pages/index.astro                                    →  /
src/pages/about.astro                                    →  /about
src/pages/contact.astro                                  →  /contact
src/pages/pricing.astro                                  →  /pricing
src/pages/privacy-policy.astro                           →  /privacy-policy
src/pages/services/seo-search-engine-optimization.astro  →  /services/seo-search-engine-optimization
src/pages/case-studies/[...page].astro                   →  /case-studies, /case-studies/2, …  (paginated)
src/pages/case-studies/[slug].astro                      →  /case-studies/:slug  (dynamic)

To add a page, create a new file in src/pages/ using the layout and structure of an existing, similar page as a starting point:

astrosrc/pages/services.astro
---
import Layout from '../layouts/Layout.astro';
import Header from '../components/Header.astro';
import Footer from '../components/Footer.astro';
---
<Layout title="Services" description="...">
  <Header current="services-seo" />
  <h1>Our Services</h1>
  <Footer />
</Layout>

Editing an Existing Page

Open the relevant file under src/pages/ and edit the markup directly. Anything imported from src/components/ lives elsewhere — edit the component file itself to change content that repeats across multiple pages (like a shared call-to-action block).

Dynamic Routes

Files wrapped in square brackets, such as [slug].astro, generate multiple pages from a single template. SeoGuru uses this for case studies, reading from the case-studies content collection (see Content Editing) rather than a raw glob import:

astrosrc/pages/case-studies/[slug].astro
---
import { getCollection } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('case-studies');
  return posts.map((post) => ({
    params: { slug: post.id },
    props: { post },
  }));
}

const { post } = Astro.props;
const { data } = post;
---

The case-study list page uses the same collection with Astro’s built-in paginate() helper to generate /case-studies, /case-studies/2, and so on, six posts per page:

astrosrc/pages/case-studies/[...page].astro
---
export async function getStaticPaths({ paginate }) {
  const posts = (await getCollection('case-studies')).sort((a, b) => a.data.order - b.data.order);
  return paginate(posts, { pageSize: 6 });
}
---

The same pattern generates the category and tag archive pages, at src/pages/case-studies/category/[category].astro and src/pages/case-studies/tag/[tag].astro.

Layouts

SeoGuru has a single shared layout, src/layouts/Layout.astro, that wraps every page’s <html> shell, <head> metadata, global stylesheet imports, and the site.js/animations.js script tags. Header and Footer are imported separately into each page rather than baked into the layout, so pages control their own header state (see Header Navigation):

astrosrc/layouts/Layout.astro
---
import '../styles/normalize.css';
import '../styles/framework.css';
import '../styles/seoguru.css';
import '../styles/custom.css';

const { title, description, ogImage = '/images/Hero-Image-2.webp' } = Astro.props;
---
<html lang="en">
  <head><title>{title}</title><!-- meta description, canonical, OG/Twitter tags --></head>
  <body>
    <slot /> <!-- Header, page content, and Footer are all passed in from the page -->
    <script src="../scripts/site.js"></script>
    <script src="../scripts/animations.js"></script>
  </body>
</html>

Page Metadata & the 404 Page

Pass a title and description into Layout on every page for correct browser tabs and search results (see also SEO); an optional ogImage prop overrides the default social preview image. SeoGuru does not currently include a 404.astro file — add one to src/pages/ if you want a custom not-found page instead of your host’s default.

06 · Guide

Components

The building blocks pages are assembled from, and how data flows into them.

Reusable Components

Components live in src/components/ and encapsulate one piece of UI so it can be reused across pages without copy-pasting markup — Header.astro, Footer.astro, ServicePage.astro (the shared shell for all three service pages), CaseStudyCard.astro, PricingSection.astro, FaqSection.astro, Testimonial.astro, WhyUs.astro, AuditCta.astro, and ArrowLink.astro. Every component is plain .astro — there’s no .tsx/React in this template.

Props & TypeScript Interfaces

Most components just destructure Astro.props directly with no declared type, which is enough for a plain-JavaScript template like this one. Where a component's prop shape benefits from documenting, an optional interface Props works the same way TypeScript interfaces do in a .tsx file — PricingSection.astro uses one for its three optional props (class, title, description), which lets each page that renders it opt into page-only styling and its own heading without affecting the other:

astrosrc/components/PricingSection.astro
---
interface Props {
  class?: string;
  title?: string;
  description?: string;
}

const { class: className, title, description } = Astro.props;
---

<section id="pricing" class:list={['pricing-section', className]}>
  <div class="w-layout-blockcontainer main-container w-container">
    <slot />
    <div class="pricing-container">
      {title && <h3 class="h2">{title}</h3>}
      {description && <p class="body-medium">{description}</p>}

In plain language: all three props are optional (the ?). The homepage renders <PricingSection title="Membership" description="Become a member today and Grow with SeoGuru" />, so it gets a centered heading above the pricing tabs; the pricing page passes neither, since it already has its own hero heading above the section (see below), and instead passes class="pricing-section--flat" for page-only background/spacing.

Component Composition

Larger sections are composed from smaller components rather than written as one long block of markup — ServicePage.astro imports and composes ArrowLink, Testimonial, WhyUs, and AuditCta internally. A component can also accept child markup through a <slot />, the way PricingSection.astro does on the pricing page — the page’s hero heading is passed in as slotted content and rendered inside the pricing section’s own wrapper, rather than living in a second, separate hero section:

astrosrc/pages/pricing.astro
<PricingSection class="pricing-section--flat">
  <div class="all-case-studies-hero-container pricing-hero-intro">
    <div class="tag"><div>Pricing</div></div>
    <h1 class="h1">Simple, Transparent SEO Pricing</h1>
  </div>
</PricingSection>

Passing Data to Components

Props are passed the same way as HTML attributes and read via Astro.props. The three service pages are the clearest example — each one builds a large data object (hero copy, images, bullet points, package details) and hands the whole thing to ServicePage, which does all the rendering:

astrosrc/pages/services/seo-search-engine-optimization.astro
---
import ServicePage from '../../components/ServicePage.astro';

const data = {
  heroTag: 'SEO - Search Engine Optimization',
  heroHeading: 'Grow Online with The ROI Driven SEO Agency',
  // ...hero image, widget stats, "why" copy and bullets, package details
};
---
<ServicePage data={data} />

To create a fourth service page, copy one of the existing src/pages/services/*.astro files, edit the values in its data object, and add a link to it in the header dropdown (see Header Navigation).

07 · Guide

Styling

Where to change colors, type, and spacing without fighting the build.

Global Styles & CSS Variables

Brand colors are defined once as CSS custom properties in src/styles/seoguru.css and referenced everywhere else. Change a value here and it updates across the whole site:

csssrc/styles/seoguru.css
:root {
  --dark: #1d1b2d;
  --body: #665f78;
  --green-light: #e4fff9;
  --green: #009d78;
  --orange: #ff4e44;
  --purple: #6341f0;
  /* plus purple-light, pink-light, yellow-light, white */
}

Four plain stylesheets are imported, in order, in Layout.astro: normalize.css (browser reset), framework.css (Webflow-derived layout/utility classes, kept so the converted markup’s class names still work), seoguru.css (this template’s actual design — colors, typography, section styling), and custom.css (small additions made after the Webflow-to-Astro conversion). When in doubt about which file to edit, start in seoguru.css.

Component Styles

Astro components support scoped <style> blocks — CSS written there only applies to that component, so class names never collide across files. SeoGuru mostly keeps styling centralized in src/styles/ rather than per component, but src/pages/pricing.astro uses a scoped, page-only <style is:global> block to adjust the shared PricingSection component’s spacing (and its cards’ shadow) without touching how it renders on the homepage; the background color still matches the homepage’s --green-light, it’s just repeated here to keep the page’s own padding override self-contained:

astrosrc/pages/pricing.astro
<style is:global>
  .pricing-section.pricing-section--flat {
    background-color: var(--green-light);
    padding-top: 270px;
  }
</style>

Responsive Design, Typography & Spacing

Breakpoints are handled with plain @media screen and (max-width: …) queries in seoguru.css (the template’s Webflow origins mean the common breakpoints are 991px, 767px, and 479px). Prefer adjusting shared values over overriding sizes on individual elements, so the site keeps a consistent rhythm as you edit it.

Avoid editing dist/

The dist/ folder is generated output — any change made there is overwritten on the next build. Always edit files under src/.

08 · Guide

Images & Assets

Everything lives in one place: public/, referenced by plain root-relative URLs.

LocationUse forBehavior
public/images/All page imagery (140 files: a mix of .svg, .webp, and .png)Copied as-is, no build-time processing
public/videos/The ambient background video used on the About pageCopied as-is
public/fonts/Self-hosted font filesCopied as-is — see Fonts & Icons
public/favicon.ico, public/webclip.pngBrowser tab icon and Apple touch icon, linked from Layout.astroCopied as-is

SeoGuru does not use src/assets/ or Astro’s built-in <Image /> component (from astro:assets) anywhere — every image is referenced directly with a plain <img> tag and a /images/… path.

Replacing Images

Swap the file in public/images/ in place (keeping the same filename), or replace the src path in the component/page markup to point at a new file:

astrosrc/components/ServicePage.astro
<img
  src={data.heroImage}
  loading="lazy"
  sizes="(max-width: 479px) 100vw, (max-width: 767px) 88vw, (max-width: 991px) 77vw, 32vw"
  srcset={data.heroImageSrcset}
  alt=""
/>

Responsive images (like the service page heroes above) supply their own hand-written srcset/sizes attributes pointing at pre-exported image sizes — there is no automatic responsive-image generation.

Supported Formats & SVGs

The template ships a mix of SVG (icons and some illustrations, several inlined directly in component markup so they can inherit color via currentColor — see the social icons in Header.astro and Footer.astro), WebP, and PNG. There is no GIF or AVIF usage.

Image Optimization

Because images are served from public/ rather than imported through astro:assets, nothing is resized, re-encoded, or compressed at build time. Images should be optimized and exported at their intended display sizes before you add them to public/images/.

Rule of thumb

Everything — hero photos, icons, and files that need a fixed URL — goes in public/ for this template. If you later want Astro to resize and hash images automatically, you’d need to move them into src/assets/ and switch the relevant <img> tags to Astro’s <Image /> component.

09 · Guide

Fonts & Icons

Swapping typefaces and icon sets without touching every component.

Google Fonts

SeoGuru does not load any fonts from Google Fonts — every typeface is self-hosted (see Local Fonts below). If you add a Google Font, the <link> tags would go in src/layouts/Layout.astro’s <head>.

Local Fonts

Three self-hosted typefaces ship in public/fonts/ and are declared with @font-face at the top of src/styles/seoguru.css:

FamilyWeightsFiles
Onest100, 300, 400, 500, 700, 900Onest-Thin.ttf through Onest-Black.ttf
Satoshi Variable300–900 (variable)Satoshi-Variable.ttf
Generalsans Variable200–700 (variable)GeneralSans-Variable.ttf
csssrc/styles/seoguru.css
@font-face {
  font-family: Onest;
  src: url('/fonts/Onest-Regular.ttf') format("truetype");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

To swap a typeface, replace the relevant .ttf files in public/fonts/, update the matching @font-face blocks, and update the font-family references used elsewhere in seoguru.css. Check each font’s license (see CREDITS.md) before redistributing it.

Icon Libraries

There is no icon library or sprite sheet. Icons are inline <svg> markup written directly in component files (for example, the social icons in Header.astro and Footer.astro, using fill="currentColor" so they inherit text color) or standalone files in public/images/ referenced with a plain <img> tag.

Replacing & Adding Icons

For an inline SVG, replace the <svg>…</svg> markup directly in the component. For an <img>-based icon, drop a new SVG or PNG into public/images/ and update the src path.

10 · Beginner Friendly

Content Editing

The fastest, lowest-risk way to make the site say what you want it to say.

New to code?

Everything in this section is plain text or attribute changes — no logic to break. Save the file, and your dev server (from Getting Started) will refresh the browser automatically so you can see the change right away.

Headings, Paragraphs & Buttons

Find the relevant page in src/pages/ (or the component it imports), locate the text between the tags, and edit it directly. Service pages keep almost all of their copy in one data object at the top of the file (see Passing Data to Components) rather than scattered through the markup:

astrosrc/pages/services/seo-search-engine-optimization.astro
// before
heroHeading: 'Grow Online with The ROI Driven SEO Agency',

// after
heroHeading: 'Rank Higher with Our Award-Winning SEO Team',

Header links live directly in src/components/Header.astro’s markup, and footer link groups live directly in src/components/Footer.astro’s markup — there is no separate navigation data file for this template. See Navigation for the full pattern.

Cards, Testimonials & Pricing

The FAQ questions shown on both the homepage and the pricing page are the one piece of repeating content driven by a shared data array — add, remove, or reword an entry and it updates everywhere FaqSection is used:

javascriptsrc/data/faqs.js
export const faqs = [
  {
    q: 'What factors determine the pricing of SEO services?',
    a: "The pricing of SEO services can be influenced by …",
  },
  // ...
];

Pricing tiers (Basic/Advanced/Custom package cards) and testimonial quotes are hand-written directly inside src/components/PricingSection.astro and src/components/Testimonial.astro respectively, rather than generated from a data array — edit those component files to change their content.

Blog Content & Contact Information

Case studies are Markdown files in src/content/case-studies/ — each file’s front matter must match the schema in content.config.js (title, images, category, tags, a client object, objectives, approach steps, and so on). Add a new file there to add a new case study; it automatically appears in the list, its category archive, and its tag archive. Contact details are currently placeholder values repeated in a few places rather than centralized in one file: the email and phone in src/pages/contact.astro, and the phone number in src/components/Header.astro. Replace all of them with your real details before launch (see Production Checklist).

12 · Guide

Forms

What ships out of the box, and what still needs a backend.

None of the three forms have an action set

SeoGuru’s audit request, contact, and newsletter forms are intentionally shipped as UI-only — no action or method attribute is set on any of them. They will not deliver anywhere until you wire each one to a form handler of your choice.

Contact Forms

Forms are plain HTML — no JavaScript form component or client-side framework is involved. All three files just have a bare <form> tag; add your own action/method to each:

astrosrc/pages/contact.astro
<form class="contact-form">

The other two are the audit-request form in src/components/AuditCta.astro and the newsletter signup form in src/components/Footer.astro.

Newsletter Forms

The footer newsletter form is not pre-wired to a mailing-list provider (Mailchimp, ConvertKit, etc.) — like the other two forms, it has no action set and only collects an email address.

Form Validation

Required fields and email format are checked in the browser using standard HTML attributes (required, type="email", maxlength) — there is no custom JavaScript validation logic.

Third-Party Form Services

None of the forms are wired to a provider out of the box. Pick a service that accepts submissions directly from static HTML (Formspree, Getform, Basin, Netlify Forms, etc.), create a form there, and add the resulting action (and method="POST") to each of the three files above — or point them at your own API route.

13 · Guide

API & Data

SeoGuru does not fetch data from any external API.

All content is local

Every page renders from data that already lives in the repository at build time: hard-coded markup and data objects in the page/component files, the faqs.js array in src/data/, and the case-studies content collection in src/content/. There is no fetch() call, API client, or environment variable anywhere in src/.

API Configuration

Not applicable — there is nothing to configure. If you add an external API integration later, see Environment Variables (.env) for the standard Astro convention for storing the URL/key.

Fetch Requests

The closest thing to a data query in this template is Astro’s built-in content collection API, used to read the case studies at build time:

astro
import { getCollection } from 'astro:content';
const posts = await getCollection('case-studies');

Data Structures

The one documented data shape in this template is the case-studies collection schema, defined with Zod in content.config.js: title, image, heroImage, category, tags (array), date, order, author, excerpt, a client object (business, industry, location, challenge), objectives (array), approach (array of { label, description }), innerImage, result, quote, and an optional quoteAuthor.

Error Handling

Not applicable — there are no runtime network requests to fail. A case-study Markdown file that doesn’t match the schema above will fail the build with a validation error, rather than fail silently at runtime.

14 · Guide

SEO

Per-page metadata, social previews, and the machine-readable files search engines expect.

Page Title & Meta Description

Pass a unique title and description into Layout on every page — these drive both the browser tab and the snippet shown in search results. Layout.astro also appends | SeoGuru to whatever title you pass:

astro
<Layout
  title="Pricing"
  description="Simple, transparent SEO pricing plans built to grow with your business."
>
<!-- renders as <title>Pricing | SeoGuru</title> -->

Canonical URL, Open Graph & Twitter/X Cards

Set inside Layout.astro’s <head>, using Astro.site (the site value from astro.config.mjs) to build absolute URLs. An optional ogImage prop overrides the site-wide default:

astrosrc/layouts/Layout.astro
---
const { title, description, ogImage = '/images/Hero-Image-2.webp' } = Astro.props;
const siteUrl = Astro.site ? new URL(Astro.url.pathname, Astro.site).toString() : Astro.url.pathname;
---
<link rel="canonical" href={siteUrl} />
<meta property="og:title" content={pageTitle} />
<meta property="og:image" content={new URL(ogImage, Astro.site ?? Astro.url.origin)} />
<meta name="twitter:card" content="summary_large_image" />

Sitemap & Robots.txt

@astrojs/sitemap is registered in astro.config.mjs and generates sitemap-index.xml automatically at build time once the site value is set to your real domain. SeoGuru does not currently include a public/robots.txt file — add one if you want to control crawler access or explicitly point crawlers at the generated sitemap:

textpublic/robots.txt
User-agent: *
Allow: /
Sitemap: https://example.com/sitemap-index.xml

Structured Data & Semantic HTML

Pages use landmark elements (<section>, <nav>) and heading hierarchy, but SeoGuru does not currently include any JSON-LD structured data. If rich search results matter to you, add Organization or Article schema to Layout.astro’s <head> (or per-page, for case studies).

15 · Guide

Deployment

Platform-by-platform steps. Confirm your rendering mode first — it changes the build output you deploy.

This is a static build

SeoGuru uses Astro’s default static output — no SSR adapter is configured. npm run build produces plain HTML/CSS/JS in dist/ that any static host can serve; you don’t need a Node-capable server at runtime.

Vercel

  1. Push the project to a GitHub, GitLab, or Bitbucket repository.
  2. In Vercel, choose Add New → Project and import that repository.
  3. Build command: npm run build · Output directory: dist — Vercel’s Astro preset detects this automatically in most cases.
  4. No environment variables are required for this template (see Environment Variables).
  5. Click Deploy.

Netlify

tomlnetlify.toml
[build]
  command = "npm run build"
  publish = "dist"

Connect the repository in the Netlify dashboard, confirm these build settings, and deploy. No environment variables are required.

Cloudflare Pages

Connect the repository, set the build command to npm run build and the output directory to dist.

GitHub Pages

Set site (and base, if deploying to a project subpath rather than a custom domain) in astro.config.mjs to match your repository URL, then build and publish the dist/ folder to the gh-pages branch (via the official astro build + a deploy action, or manually).

Traditional Hosting

Upload the contents of dist/ to your host’s public web root via FTP/SFTP or your host’s file manager — since this is a static build, any plain web host works, with no Node.js runtime required in production.

16 · Guide

Build & Production

What happens when you run build, and a checklist before you ship.

bash
npm run build     # type-check, bundle, and write dist/
npm run preview   # serve dist/ locally, exactly as it will run in production

Build Output

The build writes static HTML and the CSS/JS/image assets referenced from public/ to dist/. This folder is disposable and regenerated by every build — it’s conventional to keep it out of Git via .gitignore.

Environment Variables in Production

Not applicable — this template has no environment variables to set in any environment (see Configuration).

Production Checklist

  • Run npm run build locally and confirm it completes with no errors.
  • Run npm run preview and click through the main pages.
  • Set site in astro.config.mjs to your real deployed URL (currently the placeholder https://example.com) — it feeds the sitemap and canonical/OG tags.
  • Wire the forms in src/components/AuditCta.astro, src/components/Footer.astro, and src/pages/contact.astro to a form handler of your choice — they currently have no action set.
  • Swap the placeholder contact details — hello@example.com, +1 (555) 010-0123, the Springfield address in contact.astro, and the 1234-567-890 phone number in Header.astro — for your real ones.
  • Replace the placeholder social links (bare facebook.com/twitter.com/skype.com) in Header.astro and Footer.astro with your real profile URLs.
  • Replace the sample case studies in src/content/case-studies/ with your own.
  • Verify the favicon (public/favicon.ico), Open Graph image, and per-page meta descriptions are your own, not the template defaults.
17 · Reference

Troubleshooting

The most common problems, in the order you’re likely to hit them.

npm install fails

Cause
Node.js version too old, a corrupted local cache, or a stale lockfile from a different package manager.
Solution
Confirm node --version meets the requirement in Requirements, then run rm -rf node_modules package-lock.json && npm install.

Port already in use

Cause
Another process (often a previous dev server that didn’t shut down cleanly) is already listening on the default port.
Solution
Stop the other process, or start this one on a different port: npm run dev -- --port 4322.

Module not found

Cause
A missing dependency, or a typo in a relative import path (this template has no path aliases — every import uses a relative path like ../../components/Header.astro).
Solution
Re-run npm install; check the import path and file casing match exactly.

Images not loading

Cause
An incorrect path. Every image in this template lives in public/, so it needs a root-relative URL, not a relative one.
Solution
Review Images & Assets — reference files under public/images/ as /images/filename.webp, not ./images/filename.webp or ../public/images/filename.webp.

Styles not loading

Cause
The stylesheet isn’t imported into Layout.astro, or the CSS load order changed — normalize.css, framework.css, seoguru.css, and custom.css are imported in that specific order, and a later file can intentionally override an earlier one.
Solution
Confirm all four stylesheets are still imported at the top of src/layouts/Layout.astro, in their original order.

Editor shows a TypeScript error in a .astro file

Cause
This template isn’t written in TypeScript, but tsconfig.json still extends Astro’s strict preset, so your editor type-checks frontmatter. Most components have no declared interface, so an error is usually a genuine typo rather than a props mismatch — except in PricingSection.astro, the one component with a declared Props interface (see Props).
Solution
Read the error’s file and line number. It will not block npm run dev, but it can fail npm run build — fix it before shipping.

Build fails (but dev server works)

Cause
The dev server is more forgiving than build, which fully type-checks the project — an issue tolerated in dev can fail the build.
Solution
Read the full terminal output for the first error (later errors are often side effects of it); fix issues top to bottom and re-run.

Environment variables not working

Cause
This template ships with no .env file or environment variables at all (see Configuration), so this only applies if you’ve added your own. The usual cause is a missing PUBLIC_ prefix for a client-side value, or the dev server not being restarted after editing .env.
Solution
Rename the variable with the PUBLIC_ prefix if it’s used in browser code, then stop and restart npm run dev — env changes aren’t hot-reloaded.

Deployment problems

Cause
Wrong output directory configured on the host (it should be dist), or the site value in astro.config.mjs still pointing at the placeholder https://example.com.
Solution
Re-check the build command and output folder against Deployment for your platform, and confirm site matches your real domain (see Production Checklist).
18 · Reference

FAQ

Quick answers to the questions we hear most.

How do I install the template?

Extract the ZIP, run your package manager’s install command, then start the dev server. The full walkthrough is in Getting Started.

Which Node.js version should I use?

Node 22.12.0 or later — see Requirements. This is set in package.json’s engines field.

Does this template use TypeScript or React?

No. It’s plain Astro and vanilla JavaScript/CSS — there are no .tsx files and only one component declares an optional interface Props (see Props). The tsconfig.json that ships with it only enables editor type-checking on .astro frontmatter; there’s no TypeScript compilation step to opt out of.

How do I change colors?

Update the CSS custom properties in src/styles/seoguru.css’s :root block — see Styling.

How do I change fonts?

Swap the font import and update the corresponding --font-* variable — see Fonts & Icons.

How do I replace images?

Swap the file or update the import path — see Images & Assets.

How do I add a new page?

Add a file to src/pages/; the route is created automatically — see Pages.

How do I deploy the template?

Platform-specific steps for Vercel, Netlify, Cloudflare Pages, GitHub Pages, and traditional hosts are in Deployment.

Can I use the template commercially?

[State your license terms here — e.g. standard vs. extended license — and link to the full license text.]

Where can I get support?

See the Support section for contact details and what to include in a request.

19 · Reference

Changelog

Every notable change, newest first. Add a new entry above the last one for each release.

Version 0.0.1
  • AddedInitial release: homepage, about, contact, three service pages, a pricing page, and a case-study blog with category/tag archives, converted to Astro from the original Webflow export.
Keeping this current

Copy the markup for one entry above, update the version number, date, and bullet list, and place it at the top of this section for each new release.

20 · Reference

Support

Stuck on something this guide didn’t cover? Here’s how to reach us.

Documentation

Most questions are answered somewhere in this guide — try the search (Ctrl K) before reaching out.

Back to top

Contact support

Email hello@rnnstudio.com or reach us through our contact form.

Contact support

Support hours

We’re based in India and available Monday–Friday, 10am–7pm IST. Expect a response within 24 hours.

What to Include in a Support Request

The fastest way to get a useful answer is to include the details below in your first message:

text
When contacting support, please include:

- Template name and version
- Browser (and version)
- Operating system
- Node.js version (node --version)
- The exact error message
- Steps to reproduce the problem