Installation
Finly 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, a 60-connector integrations directory, pricing, ten customer stories, a paginated blog, nine careers adverts, about, contact, three account screens and legal pages — populated with written sample content. Configuration is what you do after you have looked around, not a prerequisite for seeing it work.
Requirements
- Node.js 22.12.0 or newer.
engines.nodeinpackage.jsonpins the floor at>=22.12.0, and the reason is the test runner rather than the language: every check undersrc/runs through Node’s--experimental-strip-types, which arrived in 22.6 and became usable without a flag dance in 22.12. Check yours withnode -v. - pnpm. The lockfile is
pnpm-lock.yamlandpnpm-workspace.yamlcarries settings that only pnpm reads.corepack enable pnpmis 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 or a domain to run the theme locally. One route — /contact/ — is server-rendered, and it renders perfectly well without an email provider configured; the action returns a readable error instead of a build failure. Everything else is a prerendered file.
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/finly.git my-fintech
cd my-fintech
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-fintech.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 ten packages, and the shape of that list is the whole design philosophy of the theme in one place:
"dependencies": {
"@astrojs/cloudflare": "^14.2.1",
"@astrojs/mdx": "^7.0.5",
"@astrojs/sitemap": "^3.7.3",
"@fontsource-variable/plus-jakarta-sans": "^5.3.0",
"@tailwindcss/vite": "^4.3.3",
"astro": "^7.2.0",
"tailwind-merge": "^3.6.0",
"tailwind-variants": "^3.3.1",
"tailwindcss": "^4.3.3"
}
Astro, Tailwind and its two class-composition helpers, one variable font, and three official Astro integrations — MDX, the sitemap and the Cloudflare adapter. sharp sits in devDependencies, where build-time image optimization belongs. There is no UI kit, no animation library, no SEO package, no icon package, no schema package, no RSS package and no CMS. Those are all implemented inside the theme rather than pulled in: the 61 UI primitives, the 91-utility motion catalog, the JSON-LD builders, the 571-icon registry, the hand-rolled RSS feed 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:
allowBuildsdenies post-install build scripts foresbuild,sharpandworkerd. None of the three needs to compile for the project to work —workerdin particular is only the local runtime binary thatwrangler devuses, and neitherastro buildnor the deployed Worker touches it. If your install prints a notice about ignored build scripts, that is this setting working as designed.minimumReleaseAgeExcludepins the Tailwind package set to the exact versions 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/and/product/integrations/— the overview plus a 60-connector directory in five categories/pricing/,/about/,/careers/,/contact//blog/,/blog/page/2/,/blog/<slug>/,/blog/category/<slug>/,/blog/author/<slug>/— thirteen posts, five authors, five categories/customers/,/customers/<slug>/,/customers/industry/<slug>/— ten stories across five industries/login/,/signup/,/forgot-password/— the three account screens, drawn without site chrome/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-four page routes and three endpoints produce 123 pages: 122 HTML files land in dist/client/ and /contact/ is served on demand. 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 61 UI primitives in every variant, alongside the motion utilities and the full 571-icon registry, grouped into nine panels — statics, disclosure, overlays, chrome, navigation, form controls, icons, motion and page-level primitives. It is the fastest way to find out what already exists rather than rebuilding 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/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.
Configuration and environment
There is no .env file to copy, and no environment variable a local run needs. Two things are configured instead, and only one of them lives outside the repository.
site in astro.config.mjs is the field only you can fill. It feeds the canonical link, og:url, hreflang, the sitemap, robots.txt, llms.txt, rss.xml and the JSON-LD @ids, so one wrong value poisons seven things at once and none of them looks broken in review. It ships pointing at the theme’s own demo deployment:
const SITE = "https://finly.domidex01.workers.dev/";
Change it in the same commit that moves the site. It is a const rather than a literal inside defineConfig because the sitemap’s customPages needs the same origin, and two copies of a URL are two chances to disagree.
Three secrets belong to the contact form, declared in the env.schema block of astro.config.mjs and all optional:
RESEND_API_KEY // resend.com -> API keys
CONTACT_TO_EMAIL // where submissions land
CONTACT_FROM_EMAIL // defaults to Resend's shared sandbox sender
They are read at request time through astro:env/server, never at build, so a fresh clone builds and runs without them — the form renders and, on submit, returns “This form is not available right now” instead of failing a build. On Cloudflare they are Worker secrets (npx wrangler secret put RESEND_API_KEY), never files. Contact Form & Email covers the whole path.
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.
src/config/en/siteData.json.ts— brand name, the home page’s title and description, the tagline, the regulatory disclaimer, the legal entity, the sample notice, the author block,sameAsand the default social image. The description is not only the home page’s: it is the site-wideWebSiteJSON-LD description on every page and the lead line of/llms.txt.src/config/en/navData.json.ts— one source for the header’s four mega-menu groups and the footer’s four columns. Every row in it resolves, and a build hook keeps it that way; read Configuration before you extend it.src/config/siteSettings.json.ts—locales,defaultLocale,localeMap, and two switches:useViewTransitionsanduseAnimations(the master switch for decorative motion).prefers-reduced-motionis honored by a global CSS guard regardless of whatuseAnimationssays; the flag is a design choice, not an accessibility one.src/config/en/legalData.json.ts— the terms and privacy copy. It is explicitly placeholder text. Have a professional review it; it is not legal advice.public/og.jpg,public/favicon.svg,public/favicon.ico— the shippedog.jpgis a labelled placeholder, and it doubles as the JSON-LDOrganizationlogo until you pass a real one, so replacing it fixes two things at once.src/data/— replace the thirteen posts, five authors and ten customer stories. Each entry is a folder whose name is the URL slug, holding anindex.md. Schemas live insrc/content.config.ts, so bad frontmatter fails the build with the entry named.
Two fields are empty on purpose and should stay empty until they are true: siteData.sameAs and siteData.author.twitter. Finly is an invented company, and every plausible value for either points at a real stranger’s account. BaseHead renders twitter:creator only when the handle is truthy, and sameAs is omitted from the Organization node entirely when the array is empty, so an unfilled field costs nothing while a wrong one misattributes the site.
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 lintruns ESLint, including the Astro JSX accessibility rules.pnpm checkrunsastro checkacross.astroand.tsfiles.pnpm buildis the real check. Content-schema errors, config typos and broken references all surface here rather than in the browser — and one of them is unique to this theme: thefinly:link-integrityhook scans the emitted HTML and fails the build naming anyhrefthat points at a path the build did not produce.pnpm testdiscovers and runs everyscripts/*.test.mjsand everysrc/**/*.test.ts/*.selfcheck.tsfile. There are fifteen today, and the runner walks both trees rather than reading a list, so a check dropped beside new logic is picked up without editing it.
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
Light is the default theme, and the device preference is deliberately ignored. BaseHead ships an inline, pre-paint script that reads localStorage("colorTheme"); only a saved "dark" — written by the ThemeToggle primitive — turns dark mode on. A first-time visitor always lands on the light theme the design was built around, whatever their OS is set to. The script stays inline on purpose; moving it into a bundled <script> reintroduces a flash of the wrong theme.
Every link on the site resolves, and the build enforces it. There is no planned-routes list and no redirect table here, because there is nothing to redirect: the footer was trimmed to the pages that exist rather than stubbed out, and finly:link-integrity fails pnpm build the moment a href points somewhere the build did not emit. Add the route before you link it — see Pages & Routing.
Troubleshooting
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 the dev server; 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 before you go looking for a bug in your markup — a pnpm build that renders correctly while dev does not is the tell.
Every image renders as alt text in dev. sharp has stopped resolving in the running dev process. Restart it; pnpm build is unaffected, which is again the tell.
Node version errors on install. The floor is 22.12.0. Older releases fail even if the install itself appears to succeed.
More in Troubleshooting.