Saasdesk 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 Saasdesk. 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.

Build tooling

No pre-built dist/ is bundled — the distributable .zip (built by gulpfile.js) ships source only. Run npm run build yourself to produce dist/.

Design assets

SVG icons and illustrations under public/images/ and src/assets/images/, plus the self-hosted General Sans variable font. No separate Figma file is bundled.

This documentation

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

Features

  • A full single-page SaaS/HR marketing site — hero, benefits, features, use cases, testimonial slider, integrations, reviews, pricing, FAQs, client logos, and a “for your team” section
  • Matching login and signup pages with shared form styling
  • Scroll-driven collapsing navbar animated with GSAP
  • Swiper-powered testimonial carousel
  • Self-hosted General Sans variable font via Astro’s Fonts API (no external font request)
  • Plain CSS design system (normalize.css, base.css, theme.css) built on CSS custom properties, so re-theming doesn’t require a build-tool config change

Technology Stack

This template is plain Astro — no React or Vue islands, no Tailwind or Sass. Interactivity is handled with small vanilla-JS helpers plus two libraries:

Astro TypeScript (strict, via tsconfig) GSAP Swiper Plain CSS

Requirements

ToolMinimum versionNotes
Node.js22.12.x or laterSet in package.json’s engines field
Package managernpm 9+, pnpm 8+, or Yarn 1.22+Use whichever you prefer; examples below cover all three
Code editor—VS Code with the 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. The template ships no additional polyfills beyond Astro/Vite’s default modern-browser build target.

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 — already nests under sasdesk-astro/
unzip sasdesk-astro.zip
cd sasdesk-astro

Node.js Setup

Check which version of Node.js is currently active:

bash
node --version
# v22.12.x 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. It’s available at http://localhost:4321 by default; the terminal output will confirm the exact address.

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/Files here are copied to the output root untouched — no optimization or hashing. Holds favicon.ico/favicon.svg, the plain CSS entry files, the local font, and section icon SVGs.
src/components/One .astro component per page section (Hero, Benefits, Features, Pricing, Navbar, Footer, etc.), plus a shared PrimaryButton.
src/layouts/Layout.astro — the common page shell: <head> tags, font preload, the Navbar, and the scroll-collapse script.
src/pages/Every file here becomes a route automatically. See Pages for details.
src/assets/Images and the local font file that should be optimized/bundled by the build. See Images & Assets.
src/styles/global.css — a small set of shared hover-animation rules that can’t live in a single component’s scoped styles. See Styling.
src/data/Structured content (navigation, pricing tiers, testimonials, FAQs, site-wide settings) kept separate from markup so it’s easy to edit without touching Astro templates.
Files not shown above

gulpfile.js packages the whole project into a distributable .zip via npm run zip — you generally won’t need to touch it unless you’re changing what ships in that archive.

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": "sasdesk-astro",
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "zip": "gulp"
  }
}

astro.config.mjs

Controls integrations, the deployed site URL, and build behavior. This template only uses it to register the self-hosted local font:

javascriptastro.config.mjs
import { defineConfig, fontProviders } from 'astro/config';

export default defineConfig({
  fonts: [
    {
      provider: fontProviders.local(),
      name: 'Generalsans Variable',
      cssVariable: '--font-sans',
      options: {
        variants: [{ weight: '200 700', style: 'normal', src: ['./src/assets/fonts/GeneralSans-Variable.ttf'] }],
      },
    },
  ],
});

There’s no site value set yet — add one before deploying (see SEO and Deployment, both of which rely on it for absolute URLs).

tsconfig.json

Extends Astro’s strict preset for type-checking .astro files and astro.config.mjs. No path aliases are configured — every import uses a relative path (e.g. ../components/Hero.astro):

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

Environment Variables (.env)

This template doesn’t define or read any environment variables out of the box — everything (copy, links, pricing) lives in src/data/ instead. If you add an integration that needs one (an API key, a form endpoint), create a .env file in the project root; variables must be prefixed PUBLIC_ to be readable in client-side code:

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

Add .env to .gitignore before creating one. Anything without the PUBLIC_ prefix should stay off the client entirely.

05 · Guide

Pages

How routes are created, and how this template’s pages are put together.

Creating a New Page

Astro uses file-based routing: any .astro or .md file placed in src/pages/ automatically becomes a route matching its file path. This template currently ships three, all flat — no nested folders or dynamic segments:

text
src/pages/index.astro   →  /
src/pages/login.astro   →  /login
src/pages/signup.astro  →  /signup

To add a page, create a new file in src/pages/ using login.astro or signup.astro as a starting point — both follow the same pattern of importing Layout and Footer directly:

astrosrc/pages/pricing-plans.astro
---
import Layout from '../layouts/Layout.astro';
import Footer from '../components/Footer.astro';
---
<Layout title="Pricing | Saasdesk" description="...">
  <h1>Pricing Plans</h1>
  <Footer />
</Layout>

Editing an Existing Page

Open the relevant file under src/pages/ and edit the markup directly. index.astro itself contains no copy — it’s just an ordered list of section components (Hero, Benefits, Features, UseCases, Testimonial, Integrations, Reviews, Pricing, FAQs, ClientLogos, ForTeam, Footer) — reorder or remove sections there, and edit each section’s actual text inside its component in src/components/ or its data file in src/data/.

Dynamic Routes

This template doesn’t currently use any — all three routes above are static files. If you add something that needs one template per item (a blog post, a plan detail page), a file wrapped in square brackets, such as [slug].astro, generates multiple pages from a single template. Astro requires a getStaticPaths() function for these on statically-generated sites:

astrosrc/pages/blog/[slug].astro
---
export async function getStaticPaths() {
  const posts = await import.meta.glob('./posts/*.md');
  return Object.keys(posts).map((path) => ({
    params: { slug: path.split('/').pop().replace('.md', '') },
  }));
}
---

Layouts

src/layouts/Layout.astro is the only layout, and every page renders through it via a <slot />. It sets up the <head> (title, description, Open Graph/Twitter tags, favicon), preloads the local font, renders <Navbar />, and includes the scroll script that collapses the header into a sticky navbar:

astrosrc/layouts/Layout.astro
---
import { Font } from 'astro:assets';
import Navbar from '../components/Navbar.astro';
import '../styles/global.css';

const { title, description } = Astro.props;
---
<html lang="en">
  <head><title>{title}</title></head>
  <body>
    <Navbar />
    <slot /> <!-- page content is injected here -->
  </body>
</html>

title is required; description is optional and, when passed, also fills the meta description and Open Graph/Twitter tags. Neither is declared with a TypeScript interface — they’re read straight off Astro.props.

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). There’s no 404.astro in this template yet — add one to src/pages/ and Astro will serve it automatically whenever no route matches.

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. Most are one-per-page-section (Hero.astro, Benefits.astro, Pricing.astro, ...), pulling their copy from a matching file in src/data/; PrimaryButton.astro is the one genuinely shared, reused-across-sections component. Every component here is plain .astro — there are no .tsx/React components or client-side islands in this template.

Props

Props are the inputs a component accepts, similar to HTML attributes. This template reads them straight off Astro.props without declaring a TypeScript interface for them — PrimaryButton.astro is a representative example:

astrosrc/components/PrimaryButton.astro
---
const { href, text } = Astro.props;
---
<a href={href} class="primary-button w-inline-block">
  <div class="button-text">{text}</div>
</a>

If you want compile-time checking for a component’s props, add an interface Props { ... } above the frontmatter destructure — the strict tsconfig.json (see Configuration) will then type-check it.

Component Composition

index.astro is itself a composition of section components with no markup of its own — each section (Hero, Benefits, Features, UseCases, Testimonial, Integrations, Reviews, Pricing, FAQs, ClientLogos, ForTeam, Footer) is dropped in as its own tag and reads its own content from src/data/. Reorder the page by reordering these tags.

Passing Data to Components

Component tags accept props the same way as HTML attributes, read via Astro.props. Most section components in this template don’t take props at all — they import their own content directly from a matching src/data/*.js file. PrimaryButton is one that does:

astro
<PrimaryButton href="/signup" text="Get Started" />
07 · Guide

Styling

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

Global Styles & CSS Variables

This template’s design tokens live in public/css/theme.css, not src/styles/ — it’s loaded as a plain <link> in Layout.astro alongside normalize.css and base.css, rather than imported into the Astro build. Colors are CSS custom properties on :root:

csspublic/css/theme.css
:root {
  --color--grey: #6a6a7a;
  --color--accent: #00acca;
  --color--dark: #2b2b46;
  --color--blue: #e6fbff;
  --color--green: #e6fff0;
  --color--purple: #e6e8ff;
}

src/styles/global.css is much smaller by comparison — it only holds the shared hover-animation rules for .secondary-button variants that appear in multiple components and so can’t live in any one component’s scoped <style> block.

Component Styles

Astro components support scoped <style> blocks — CSS written there only applies to that component, so class names never collide across files. Most components in this template (e.g. PrimaryButton.astro) keep their own hover/interaction styles this way:

astrosrc/components/PrimaryButton.astro
<style>
  .primary-button:hover .button-arrow-bg { transform: scale(3.75); }
</style>

Responsive Design, Typography & Spacing

Breakpoints, spacing, and the type scale are defined in public/css/theme.css and base.css rather than global.css. Prefer adjusting these shared values over overriding sizes on individual elements, so the site keeps a consistent rhythm as you edit it. Fonts use the CSS variable set up by Astro’s Fonts API — see Fonts & Icons.

Avoid editing dist/

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

08 · Guide

Images & Assets

Two homes for static files, and when to use each.

LocationUse forBehavior
src/assets/Images referenced inside components or pagesContent-hashed by the build; resized/re-encoded too, but only for images rendered through the Image component (not the plain <img src={x.src}> pattern used for some icons — see below)
public/Files that need a fixed, predictable URL (favicons, downloadable PDFs, og-image.jpg)Copied as-is, no processing

Replacing Images

Components like Hero.astro import a photo from src/assets/images/ and render it through the optimized Image component — swap the file in place (keeping the same filename) or update the import to point at your new file:

astrosrc/components/Hero.astro
---
import { Image } from 'astro:assets';
import heroImage from '../assets/images/hero-image.webp';
---
<Image src={heroImage} alt="..." />

Icons and simpler UI images (like the checkmark on the login/signup pages) instead import the file as a plain module and read its resolved URL off .src, rendering a bare <img> rather than the Image component:

astrosrc/pages/login.astro
---
import checkmarkIcon from '../assets/images/checkmark.svg';
---
<img src={checkmarkIcon.src} loading="lazy" alt="check mark" />

Most other icons live directly in public/images/ and are referenced by a plain root-relative path (e.g. /images/benefit-icon-1.svg) with no import at all.

Supported Formats & SVGs

JPEG, PNG, WebP, AVIF, and GIF are supported through the optimized Image component (this template’s hero and section photos are .webp). SVG icons are imported as plain modules (see above) or referenced directly from public/images/ as static files.

Image Optimization

Images imported from src/assets/ are automatically resized and re-encoded at build time based on the width/height (or densities/widths) you specify — you generally don’t need a separate compression step before adding an image to the project.

Rule of thumb

If an image is part of the design (hero photos, thumbnails, icons), put it in src/assets/. If a URL needs to work regardless of the build (a favicon, a file linked from an email), put it in public/.

09 · Guide

Fonts & Icons

Swapping typefaces and icon sets without touching every component.

Local Fonts

This template loads no Google (or other remote) fonts — its one typeface, General Sans, is self-hosted and registered through Astro’s built-in Fonts API instead of a manual @font-face block:

javascriptastro.config.mjs
fonts: [
  {
    provider: fontProviders.local(),
    name: 'Generalsans Variable',
    cssVariable: '--font-sans',
    options: { variants: [{ weight: '200 700', style: 'normal', src: ['./src/assets/fonts/GeneralSans-Variable.ttf'] }] },
  },
]

The variable font file lives at src/assets/fonts/GeneralSans-Variable.ttf and is preloaded in Layout.astro with <Font cssVariable="--font-sans" preload />; use the resulting var(--font-sans) anywhere in CSS. To swap the typeface, replace the font file and update name/src here — no @font-face rule to touch.

Icon Libraries

No icon library is used. Every icon is a standalone SVG file under public/images/ (referenced by root-relative path) or src/assets/images/ (imported and rendered via <img src={icon.src}> or the Image component — see Images & Assets).

Replacing & Adding Icons

Swap the SVG file in place (keeping the filename) to replace an icon, or add a new SVG to public/images/ (for a root-relative reference) or src/assets/images/ (to import it) and reference it the same way as its neighbors.

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 component in src/components/, locate the text between the tags, and edit it directly — e.g. the hero heading and CTA in Hero.astro:

astrosrc/components/Hero.astro
<!-- before -->
<h1>All-in-one Payroll and HR System for small team</h1>
<PrimaryButton href="/signup" text="Get Started" />

<!-- after -->
<h1>Everything Your HR Team Needs</h1>
<PrimaryButton href="/signup" text="Start Free Trial" />

Header links are hardcoded directly in Navbar.astro’s markup; footer links are data-driven from src/data/footer.js. See Navigation for the full pattern and why the two differ.

Cards, Testimonials & Pricing

Repeating content blocks (reviews, pricing tiers, FAQs, benefits) are driven by a data array in src/data/ rather than hand-written per card, so adding or removing an item is a matter of editing the list:

javascriptsrc/data/reviews.js
export const reviews = [
  {
    image: clientImage3,
    stat: '3x',
    statLabel: 'Faster Hiring',
    quote: 'They have a great understanding of HR strategies...',
    name: 'Adam Smith',
    title: 'Co-Founder & CEO',
  },
  // ...
];

The matching .astro component (e.g. Reviews.astro, Pricing.astro, FAQs.astro) imports this array and loops over it — you rarely need to touch the component itself just to change copy.

Contact Information

There’s no blog in this template. Contact details (email, phone) and social/app-store links live in one place, src/data/site-config.js, and are imported wherever they’re shown (e.g. the footer):

javascriptsrc/data/site-config.js
export const siteConfig = {
  name: 'Saasdesk',
  contact: { email: 'hello@example.com', phone: '123-456-7890' },
  social: { facebook: '...', instagram: '...', linkedin: '...' },
};
12 · Guide

Forms

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

Forms need somewhere to go

This template has no newsletter or contact form — only the login and signup forms below. Neither is wired to a backend, an action URL, or a submit handler; submitting either does nothing yet. Connect them to your own auth backend or a third-party service before shipping.

Login & Signup Forms

src/pages/login.astro and signup.astro each render a plain HTML <form> with named inputs and no JavaScript submit handler:

htmlsrc/pages/login.astro
<form name="wf-form-Login-Form" class="utility-form">
  <input class="input-field w-input" name="Work-Email" type="email" required />
  <input class="input-field w-input" name="Password" type="password" required />
  <input type="submit" class="submit-button w-button" value="Login" />
</form>

To make these functional, add an action pointing at your auth endpoint, or attach a submit handler in an inline <script> (see Navigation for how this template already does that pattern elsewhere) that calls your own API or a third-party auth provider.

Form Validation

Both forms rely entirely on native HTML attributes (required, type="email", maxlength) for validation — there’s no custom JavaScript validation function to extend or replace.

Third-Party Form Services

Since neither form currently submits anywhere, services like Formspree, Getform, or Basin (which accept submissions directly from static HTML) are a fast way to make them functional — typically just adding an action attribute pointing at the service.

13 · Guide

API & Data

Not applicable to this template — noted here for completeness.

No external data source

This template makes no fetch calls and reads no environment variables. Every piece of content (pricing, reviews, FAQs, benefits, navigation) is a static array in src/data/*.js, imported directly by the component that renders it — see Content Editing. If you later wire this up to a real API or CMS, that's the place each affected component would switch from a static import to a fetch call.

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, and Layout.astro automatically mirrors them into Open Graph and Twitter tags too:

astro
<Layout
  title="Saasdesk - All-in-one Payroll and HR System for small teams"
  description="Helping them establish efficient HR operations, manage growing teams, and stay compliant."
>

Open Graph & Twitter/X Cards

Layout.astro already sets og:type, og:title, og:description, twitter:card, twitter:title, and twitter:description from the same title/description props. Two things it does not set yet: a canonical <link> and an og:image — add both once you have a real domain and a social preview image:

htmlsrc/layouts/Layout.astro
<link rel="canonical" href={Astro.url.href} />
<meta property="og:image" content="[absolute URL to a social preview image]" />

Sitemap & Robots.txt

Neither exists yet in this template — there’s no public/robots.txt and Astro’s official sitemap integration (@astrojs/sitemap) isn’t installed. To add a sitemap, run astro add sitemap and set a site value in astro.config.mjs (see Configuration); to add crawler rules, create public/robots.txt:

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

Structured Data & Semantic HTML

Use landmark elements (<header>, <nav>, <main>, <article>) and a single <h1> per page so both search engines and screen readers can parse the page’s structure. Add JSON-LD structured data in the layout’s <head> for rich results (e.g. Organization or Article schema) where relevant to your content.

15 · Guide

Deployment

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

This template builds fully static

No SSR adapter is configured, so npm run build produces plain HTML/CSS/JS in dist/ that any static host can serve as-is — no Node process required 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 (Astro static) — Vercel’s Astro preset detects this automatically in most cases.
  4. Add any environment variables from your .env file under Project Settings → 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, add environment variables under Site configuration → Environment variables, and deploy.

Cloudflare Pages

Connect the repository, set the build command to npm run build and the output directory to dist. For SSR Astro sites, use the official @astrojs/cloudflare adapter instead of the static preset.

GitHub Pages

Set site and base 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). GitHub Pages only serves static output — it cannot run an SSR adapter.

Traditional Hosting

For a static build, upload the contents of dist/ to your host’s public web root via FTP/SFTP or your host’s file manager. For an SSR build or a React/Vite app that needs a Node process, the host must support running node continuously (a VPS or a Node-compatible host) — plain shared hosting without Node support cannot run it.

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, hashed CSS/JS bundles, and optimized images to dist/ (or build/, for some Vite/React setups). This folder is disposable and regenerated by every build — it’s conventional to keep it out of Git via .gitignore.

Environment Variables in Production

Variables from your local .env are not deployed automatically — each hosting platform requires you to set them again in its dashboard (see Deployment). A missing production variable is one of the most common causes of a build that works locally but fails, or behaves unexpectedly, once deployed.

Production Checklist

  • Run npm run build locally and confirm it completes with no errors.
  • Run npm run preview and click through the main pages.
  • Confirm the site URL in astro.config.mjs matches your real domain.
  • Set every environment variable from .env in your hosting platform’s dashboard.
  • Check that forms, if any, point at a real endpoint rather than a placeholder.
  • Verify favicon, Open Graph image, and 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 uses no tsconfig.json path aliases, so every import is a real relative path.
Solution
Re-run npm install; check the import path and casing matches the file’s actual location exactly.

Images not loading

Cause
An incorrect relative path, or an image placed in src/assets/ but referenced with a public/-style absolute path (or vice versa).
Solution
Review Images & Assets — imported assets need an import statement; public/ files need a root-relative URL like /logo.svg.

Styles not loading

Cause
One of the plain <link> tags in Layout.astro (normalize.css, base.css, theme.css) is missing, or a component’s scoped <style> block has a typo in a class name it targets.
Solution
Confirm all three stylesheet <link> tags are present in Layout.astro’s <head>; check the class name in the component markup matches the one in its <style> block exactly.

TypeScript errors

Cause
Most errors here come from the strict tsconfig.json type-checking an .astro file itself (e.g. an unhandled null), rather than a props mismatch — components in this template don’t declare a typed interface for their props (see Props).
Solution
Read the error’s file and line number; if you’ve added your own interface Props to a component, compare it against how that component is actually called.

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
Missing the PUBLIC_ prefix for a client-side value, or the dev server wasn’t 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, or environment variables set locally but not in the platform’s dashboard.
Solution
Re-check the build command and output folder against Deployment for your platform, and confirm every variable from .env is duplicated there.
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 or later — this is enforced by the engines field in package.json, not just a recommendation. See Requirements.

Does this template use TypeScript?

Only for type-checking — tsconfig.json extends Astro’s strict preset, but every data file is plain .js and no component declares a typed Props interface. See Configuration.

How do I change colors?

Update the CSS custom properties in public/css/theme.css — 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.
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