Skip to content
AstroCraft Docs
On this theme

Installation

Urengi ships as a complete Astro project, not as an npm package or an integration you register. You clone the repository, install its dependencies, and start editing — there is no theme layer between you and the markup, and nothing is hidden inside node_modules. Every component, token, icon and animation is a file in src/ that you own from the first commit.

It runs with no configuration, no API keys and no accounts. pnpm install followed by pnpm dev gives you the whole site — home, product, pricing, about, contact, a paginated blog, seven customer stories, a 24-entry integrations directory, sign-in and sign-up, and legal pages — populated with sample content. Configuration is what you do after you have looked around, not a prerequisite for seeing it work.

Requirements

  • Node.js 22.13.0 or newer. This is enforced by engines.node in package.json. The floor is set by pnpm 11 rather than by the language: the test runner executes TypeScript directly through Node’s --experimental-strip-types, which works from 22.12. Check yours with node -v.
  • pnpm. packageManager pins [email protected], so with Corepack enabled the right version is selected for you. corepack enable pnpm is the shortest route if you do not have it.
  • Git, to clone the repository and to keep your own history from the first commit onward.

You do not need a hosting account, an email provider, a CMS or a domain to run the theme locally. Urengi is a fully static site with no adapter — nothing renders on demand, which is why the local requirements stop here.

Get the code

Clone the repository and drop the upstream history so your project starts with a clean log:

git clone https://github.com/Astro-Craft-Theme/urengi.git my-saas
cd my-saas
rm -rf .git && git init

If you would rather keep the upstream remote so you can pull theme updates later, skip the rm -rf .git and rename the remote instead:

git remote rename origin upstream
git remote add origin [email protected]:you/my-saas.git

Keeping upstream is only worth it if you plan to merge theme changes. Because the theme is source you edit directly, a merge after heavy customization is a real merge — most people take the clean-slate route and treat the clone as a starting point.

Install dependencies

pnpm install

The runtime dependency list is nine packages, and the shape of that list is the whole design philosophy of the theme in one place:

"dependencies": {
  "@astrojs/mdx": "^7.0.0",
  "@astrojs/sitemap": "^3.7.3",
  "@fontsource-variable/host-grotesk": "^5.3.0",
  "@tailwindcss/vite": "^4.3.2",
  "astro": "^7.0.3",
  "sharp": "^0.35.3",
  "tailwind-merge": "^3.6.0",
  "tailwind-variants": "^3.2.2",
  "tailwindcss": "^4.3.2"
}

Astro, Tailwind and its two class-composition helpers, one variable font, sharp for build-time image optimization, and two official Astro integrations. There is no UI kit, no animation library, no SEO package, no icon package, no schema package and no CMS. Those are all implemented inside the theme rather than pulled in — the 44 UI primitives, the 91-utility motion catalog, the JSON-LD builders, the 576-icon registry and the typed config layer are source files in src/. That is why the surface you have to keep up to date stays small, and it is also why customizing any of them means editing a file rather than reading a plugin’s options.

Two settings in pnpm-workspace.yaml are worth knowing about before they surprise you:

  • allowBuilds denies post-install build scripts for @parcel/watcher, esbuild and sharp. None of the three needs to compile for the project to work, and denying them keeps installs fast and free of native toolchain requirements. If your install prints a notice about ignored build scripts, that is this setting working as designed.
  • minimumReleaseAgeExclude pins the Tailwind packages to the exact version set the theme was built and tested against, including every platform-specific @tailwindcss/oxide-* binary.

Start the dev server

pnpm dev

The site is at http://localhost:4321. Every route is available immediately with sample content in place:

  • / — the home page
  • /product/, /pricing/, /about/, /contact/
  • /blog/, /blog/page/<n>/, /blog/<slug>/, /blog/category/<slug>/ — eight sample posts, four authors, four categories
  • /customers/, /customers/page/<n>/, /customers/<slug>/, /customers/industry/<slug>/ — seven customer stories across five industries
  • /integrations/, /integrations/<slug>/, /integrations/category/<slug>/ — a 24-entry directory in nine categories
  • /signin/ and /signup/
  • /privacy/ and /terms/ — placeholder legal copy
  • /rss.xml, /robots.txt, /llms.txt, /sitemap-index.xml — all generated, all deriving their absolute URLs from one setting

Twenty-five route files produce 72 built pages, because seven of those files are dynamic: two paginate, three build one page per taxonomy label, and three build one page per collection entry. The full map is in Pages & Routing.

The primitive catalog

Before you start building pages, open http://localhost:4321/examples/ui. It is a development-only catalog rendering all 44 UI primitives in every variant, alongside the motion utilities and the full 576-icon registry, grouped into eight panels — Tier 1, Tier 2, Tier 3, the V2 batch, navigation, advanced form controls, icons and motion. It is the fastest way to find out what already exists rather than rebuilding it, and about twenty of the primitives are used by no page in the theme: that is inventory for the site you build next, and this is how you shop it.

The route is gated inside getStaticPaths:

export function getStaticPaths() {
  return import.meta.env.PROD ? [] : [{ params: { catalog: "ui" } }];
}

A production build emits no paths for it, so no HTML ships. You can leave it in place while you work. When you no longer need it, delete src/pages/examples/ and src/components/Sections/UiCatalog/ together — Tailwind still scans the catalog’s markup even though it builds no pages, so its demo classes sit in the stylesheet every real page loads. With the catalog present the shared stylesheet is 118,821 bytes carrying 96 @keyframes; the site’s own pages reach for about nineteen of the catalog’s ninety-one animations. See Deployment for the full pre-launch list.

Environment variables

One variable matters, and it is not required locally. Copy the example file if you want a place to keep it:

cp .env.example .env
  • SITE_URL — your production domain. It feeds the canonical link, Open Graph tags, JSON-LD, the sitemap, robots.txt, llms.txt and the RSS feed, so one wrong value poisons six things at once and none of them looks broken in review. It defaults to https://example.com.
  • DEPLOY_ENV — only needed on hosts that are neither Netlify nor Vercel. See below.
  • The Resend pairsRESEND_API_KEY, RESEND_AUDIENCE_ID and a *_FROM / *_TO pair per form. All optional, all unused until you connect a form. Forms & Email covers them.

A fresh clone builds and runs on the placeholder domain, deliberately, so you can see the site before you own a domain. What you cannot do is deploy on it — astro.config.mjs throws:

const site = process.env.SITE_URL ?? "https://example.com";
const isProductionDeploy =
  process.env.CONTEXT === "production" ||        // Netlify
  process.env.VERCEL_ENV === "production" ||     // Vercel
  process.env.DEPLOY_ENV === "production";       // anything else

if (isProductionDeploy && site.includes("example.com")) {
  throw new Error("SITE_URL is unset or still the placeholder. …");
}

The theme is host-agnostic, so “is this a production deploy?” reads each host’s own build variable rather than assuming one. None of those three is set by a local pnpm build or by a deploy preview, so previews and local work build freely. On a host not listed there, set DEPLOY_ENV=production yourself — the gate is only as good as the signal it can see, and an unset signal means the guard silently does nothing.

Make it yours

With the site running, these are the first files to edit. All of them are typed, so a mistake fails the build with the offending value named rather than shipping quietly.

  1. src/config/siteData.json.ts — brand name, the home page’s title, the site description, the author block, sameAs and the default social image. The description is not only the home page’s: it is the site-wide WebSite JSON-LD description on all 72 pages and the lead line of /llms.txt.
  2. src/config/navData.json.ts — one source for the navbar and the footer. Read the Configuration chapter before editing this one; the two surfaces answer deliberately different questions.
  3. src/config/siteSettings.json.tssiteLang, siteLocale, and two switches: useViewTransitions and useAnimations (the master switch for decorative motion). prefers-reduced-motion is honored by a global CSS guard regardless of what useAnimations says; the flag is a design choice, not an accessibility one.
  4. src/config/legalData.json.ts — the terms and privacy copy. It is explicitly placeholder text. Have a professional review it; it is not legal advice.
  5. public/og.jpg, public/favicon.svg, public/favicon.ico — the shipped og.jpg is a placeholder, and it doubles as the JSON-LD Organization logo until you pass a real one, so replacing it fixes two things.
  6. src/data/ — replace the eight posts, four authors, seven customer stories and 24 integrations. Each entry is a folder whose name is the URL slug, holding an index.md. Schemas live in src/content.config.ts, so bad frontmatter fails the build with the entry named.

Colors, type and spacing are not in that list because they are not in the config layer. They are CSS tokens in src/styles/tailwind-theme.css and src/styles/global.css, and the Colors & Theming and Typography chapters cover them.

Verify your install

pnpm lint && pnpm check && pnpm build && pnpm test && pnpm wiki:lint
  • pnpm lint runs ESLint, including the Astro JSX accessibility rules.
  • pnpm check runs astro check across .astro and .ts files.
  • pnpm build is the real check. Content-schema errors, config typos and broken references all surface here rather than in the browser.
  • pnpm test runs every *.test.ts file under src/ with Node’s type stripping — no framework, no fixtures, nothing to register. There are twenty-six today. If it ever finds zero it fails rather than passing, so the suite cannot quietly disappear.
  • pnpm wiki:lint does the same job for the wiki/ knowledge base: it resolves every path:line citation and checks the cited line still contains the symbol the prose names.

There is also pnpm format, which runs eslint --fix and then Prettier. Class ordering is handled by prettier-plugin-tailwindcss, so let it sort and do not hand-order class lists.

Two things to know before you start building

The theme follows the device, and a saved pick wins. BaseHead ships an inline, pre-paint script that reads localStorage("colorTheme"); if nothing is pinned it follows prefers-color-scheme and keeps following live OS changes. The ThemeToggle primitive is what writes that key. The script stays inline on purpose — moving it into a bundled <script> reintroduces a flash of the wrong theme.

Seventeen designed routes do not exist yet, and they redirect rather than 404. navData.columns ships the design’s complete footer directory, and aboutData, homeData and productData link careers, the trust centre and the product sub-pages. Every one of those hrefs is listed in src/config/plannedRoutes.json.ts and redirected to / by astro.config.mjs, so nothing on a live demo dead-ends. Build the page, then delete its entry — plannedRoutes.test.ts fails if a redirect and a real page ever claim the same route.

Troubleshooting

The build throws about SITE_URL. You are running a production deploy with the placeholder domain still in place. Set SITE_URL in your host’s environment variables. This is intentional — it makes it impossible to ship canonical URLs and a sitemap pointing at example.com.

A renamed content entry keeps 404ing in dev. Astro’s content layer caches entries, and moving or renaming a folder while the dev server runs can leave the old entry in place and the new one missing. Restart pnpm dev; touching the file will not clear it.

A utility class has no effect in dev. In a long-running dev server, classes used only in newly created files can be missing from the generated stylesheet. Restart the dev server before you go looking for a bug in your markup.

Node version errors on install. The floor is 22.13.0. Older releases fail even if the install itself appears to succeed.

More in Troubleshooting.

NEXT STEPProject Structure