52 Commits

Author SHA1 Message Date
423e8d90ee fix(deps): update react monorepo
Some checks are pending
Lint / Lint and Typecheck (push) Waiting to run
2025-08-14 08:02:51 +00:00
d58a5bb2bb fix(deps): update dependency zod to v4.0.17
All checks were successful
Lint / Lint and Typecheck (push) Successful in 49s
2025-08-09 18:02:49 +00:00
2740e57dd0 fix(deps): update dependency lucide-react to ^0.539.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 45s
2025-08-09 04:04:09 +00:00
e45c437015 chore(deps): update dependency typescript-eslint to v8.39.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 46s
2025-08-09 03:05:01 +00:00
033088185c chore(deps): update dependency eslint to v9.33.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 47s
2025-08-09 02:05:01 +00:00
9aa7a7e4a2 fix(deps): update dependency zod to v4.0.16
All checks were successful
Lint / Lint and Typecheck (push) Successful in 45s
2025-08-09 01:05:16 +00:00
02c87ec8bf chore(deps): update dependency @types/node to v22.17.1
All checks were successful
Lint / Lint and Typecheck (push) Successful in 50s
2025-08-09 00:04:00 +00:00
061942aa8e fix(deps): update dependency react-hook-form to v7.62.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 53s
2025-08-02 06:06:04 +00:00
5e4aca4ec2 fix(deps): update dependency lucide-react to ^0.536.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 48s
2025-08-02 05:06:01 +00:00
2451c43e73 chore(deps): update pnpm to v10.14.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 44s
2025-08-02 04:06:13 +00:00
bf75fe7b2b chore(deps): update dependency typescript to v5.9.2
All checks were successful
Lint / Lint and Typecheck (push) Successful in 43s
2025-08-02 03:09:20 +00:00
375e93ce59 chore(deps): update dependency @types/node to v22.17.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 43s
2025-08-02 02:16:41 +00:00
832e2cdd3d fix(deps): update dependency zod to v4.0.14
All checks were successful
Lint / Lint and Typecheck (push) Successful in 42s
2025-08-02 01:07:36 +00:00
16d4dd53a1 fix(deps): update dependency @hookform/resolvers to v5.2.1
All checks were successful
Lint / Lint and Typecheck (push) Successful in 42s
2025-08-02 00:05:33 +00:00
1936fdff56 FAQ json-ld
All checks were successful
Lint / Lint and Typecheck (push) Successful in 54s
2025-07-27 15:20:07 +02:00
4dcbdc4366 fix(deps): update nextjs monorepo to v15.4.4
All checks were successful
Lint / Lint and Typecheck (push) Successful in 53s
2025-07-27 12:02:20 +00:00
418e1fcf52 fix(deps): update dependency lucide-react to ^0.526.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 47s
2025-07-26 20:02:34 +00:00
2585e6fca9 fix(deps): update dependency react-hook-form to v7.61.1
All checks were successful
Lint / Lint and Typecheck (push) Successful in 48s
2025-07-26 05:03:32 +00:00
031e5bb50e fix(deps): update dependency @hookform/resolvers to v5.2.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 48s
2025-07-26 04:04:11 +00:00
65711a7ad6 chore(deps): update dependency typescript-eslint to v8.38.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 48s
2025-07-26 03:04:45 +00:00
1ee037e5cc chore(deps): update dependency eslint to v9.32.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 54s
2025-07-26 02:04:47 +00:00
fa35db17e0 fix(deps): update dependency zod to v4.0.10
All checks were successful
Lint / Lint and Typecheck (push) Successful in 48s
2025-07-26 01:05:31 +00:00
de7507323e chore(deps): update dependency tw-animate-css to v1.3.6
All checks were successful
Lint / Lint and Typecheck (push) Successful in 50s
2025-07-26 00:03:47 +00:00
9f1f4e1ba6 chore(deps): update dependency @types/node to v22.16.5
All checks were successful
Lint / Lint and Typecheck (push) Successful in 50s
2025-07-19 01:05:06 +00:00
c0a1ec0ec6 chore(deps): update dependency typescript-eslint to v8.37.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 53s
2025-07-19 00:04:02 +00:00
1ca5545db7 nextjs upgrade
All checks were successful
Lint / Lint and Typecheck (push) Successful in 50s
2025-07-15 14:38:49 +02:00
bd914b05dd chore(deps): pin dependencies (#7)
All checks were successful
Lint / Lint and Typecheck (push) Successful in 46s
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [eslint-plugin-react-hooks](https://react.dev/) ([source](https://github.com/facebook/react/tree/HEAD/packages/eslint-plugin-react-hooks)) | devDependencies | pin | [`^5.2.0` -> `5.2.0`](https://renovatebot.com/diffs/npm/eslint-plugin-react-hooks/5.2.0/5.2.0) |
| [pnpm/action-setup](https://github.com/pnpm/action-setup) | action | pinDigest |  -> `a7487c7` |

Add the preset `:preserveSemverRanges` to your config if you don't want to pin your dependencies.

---

### Configuration

📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MS4zMS4xIiwidXBkYXRlZEluVmVyIjoiNDEuMzEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Reviewed-on: #7
Co-authored-by: Renovate Bot <renovatebot@schulze.network>
Co-committed-by: Renovate Bot <renovatebot@schulze.network>
2025-07-13 08:36:45 +02:00
fc452ebd4c pnpm ci
All checks were successful
Lint / Lint and Typecheck (push) Successful in 54s
2025-07-13 01:37:43 +02:00
201c1ee523 pnpm CI
Some checks failed
Lint / Lint and Typecheck (push) Failing after 5s
2025-07-13 01:35:03 +02:00
1c1b842a15 PNPM
Some checks failed
Lint / Lint and Typecheck (push) Failing after 6s
2025-07-13 01:15:15 +02:00
aba4e4a7f6 npm audit fix
All checks were successful
Lint / Lint and Typecheck (push) Successful in 44s
2025-07-13 00:58:58 +02:00
4dcd24f1fd fix(deps): update dependency zod to v4 (#6)
Some checks failed
Lint / Lint and Typecheck (push) Has been cancelled
This PR contains the following updates:

| Package | Change | Age | Confidence |
|---|---|---|---|
| [zod](https://zod.dev) ([source](https://github.com/colinhacks/zod)) | [`^3.24.3` -> `^4.0.0`](https://renovatebot.com/diffs/npm/zod/3.25.76/4.0.5) | [![age](https://developer.mend.io/api/mc/badges/age/npm/zod/4.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/zod/3.25.76/4.0.5?slim=true)](https://docs.renovatebot.com/merge-confidence/) |

---

### Release Notes

<details>
<summary>colinhacks/zod (zod)</summary>

### [`v4.0.5`](https://github.com/colinhacks/zod/releases/tag/v4.0.5)

[Compare Source](https://github.com/colinhacks/zod/compare/v4.0.4...v4.0.5)

#### Commits:

- [`f91a73e`](f91a73ec23) Support pipes in discriminated unions. Closes [#&#8203;4856](https://github.com/colinhacks/zod/issues/4856) ([#&#8203;4861](https://github.com/colinhacks/zod/issues/4861))
- [`45afab0`](45afab0f84) 4.0.5

### [`v4.0.4`](https://github.com/colinhacks/zod/releases/tag/v4.0.4)

[Compare Source](https://github.com/colinhacks/zod/compare/v4.0.3...v4.0.4)

#### Commits:

- [`9335f05`](9335f0543d) Adds `ZodFirstPartyTypeKind` stub to fix module resolution failure inside `zod-to-json-schema`

### [`v4.0.3`](https://github.com/colinhacks/zod/releases/tag/v4.0.3)

[Compare Source](44a936cb77...v4.0.3)

#### Commits:

- [`5905a8d`](5905a8d810) Improve check-versions script
- [`f3e749b`](f3e749b1b0) Remove global File interface
- [`44a936c`](44a936cb77) 4.0.2
- [`74006ed`](74006edd49) Fix JSR provenance
- [`ff4af5e`](ff4af5e889) 4.0.3
- [`ce573e8`](ce573e8799) Update test badge
- [`9a7161a`](9a7161a976) Fix versions

### [`v4.0.2`](https://github.com/colinhacks/zod/compare/v4.0.1...44a936cb77961e57a0988d8a3c63d9c71fce69ac)

[Compare Source](https://github.com/colinhacks/zod/compare/v4.0.1...44a936cb77961e57a0988d8a3c63d9c71fce69ac)

### [`v4.0.1`](https://github.com/colinhacks/zod/releases/tag/v4.0.1): v4.0.0

[Compare Source](79d4d80e3b...v4.0.1)

With this release, `zod@4.0.0` has been published to `npm`. There were no code changes between 3.25.76 and 4.0.0!

Zod 4 has been stable for the past 6 weeks, but it was published inside zod@3.25.x on npm. this transitionary window gave the ecosystem time to incrementally support for Zod 4 (without dropping support for Zod 3). As there is now near-universal support for Zod 4 in the ecosystem, ths time feels right to finally put a bow on things 🎀

To upgrade to Zod 4:

```
npm upgrade zod@^4.0.0
```

If you’ve already migrated to Zod 4 using the subpaths, there are no changes required. however you can optionally simplify your imports (recommended)

```ts
// after upgrading to zod@4.0.0:
import * as z from "zod"; // Zod 4 (regular)
import * as z from "zod/mini" // Zod 4 Mini

// these still work, but are no longer needed
import * as z from "zod/v4";
import * as z from "zod/v4-mini":

// if you still need Zod 3
import * as z from "zod/v3"; // Zod 3
```

**Library authors** — if you've already implemented Zod 4 support according to the best practices outlined in the [Library authors](/library-authors) guide, bump your peer dependency to include `zod@^4.0.0`:

```json
// package.json
{
  "peerDependencies": {
    "zod": "^3.25.0 || ^4.0.0"
  }
}
```

*There should be no other code changes necessary.* No code changes were made between the latest `3.25.x` release and `4.0.0`. This does not require a major version bump.

### [`v4.0.0`](https://github.com/colinhacks/zod/compare/v3.25.76...79d4d80e3b47f04752c5c281077b53f889551441)

[Compare Source](https://github.com/colinhacks/zod/compare/v3.25.76...79d4d80e3b47f04752c5c281077b53f889551441)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - "every weekend" (UTC), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0MC40Ni4wIiwidXBkYXRlZEluVmVyIjoiNDEuMzEuMSIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Felix Schulze <admin@schulze.network>
Reviewed-on: #6
Co-authored-by: Renovate Bot <renovatebot@schulze.network>
Co-committed-by: Renovate Bot <renovatebot@schulze.network>
2025-07-13 00:58:35 +02:00
440b759daa chore(deps): update dependency typescript-eslint to v8.36.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 47s
2025-07-12 04:02:44 +00:00
9a54bdf93f chore(deps): update dependency eslint to v9.31.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 45s
2025-07-12 03:03:16 +00:00
cabcbbb84a fix(deps): update dependency zod to v3.25.76
All checks were successful
Lint / Lint and Typecheck (push) Successful in 47s
2025-07-12 02:03:43 +00:00
d815cab9d8 chore(deps): update dependency prettier-plugin-tailwindcss to v0.6.14
All checks were successful
Lint / Lint and Typecheck (push) Successful in 44s
2025-07-12 01:04:18 +00:00
be6a875999 chore(deps): update dependency @types/node to v22.16.3
All checks were successful
Lint / Lint and Typecheck (push) Successful in 52s
2025-07-12 00:03:12 +00:00
31c6c7106f fix(deps): update dependency react-hook-form to v7.60.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 50s
2025-07-05 06:01:54 +00:00
93c1320651 chore(deps): update dependency @types/node to v22.16.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 44s
2025-07-05 05:02:43 +00:00
b875d65fdc fix(deps): update nextjs monorepo to v15.3.5
All checks were successful
Lint / Lint and Typecheck (push) Successful in 48s
2025-07-05 04:03:01 +00:00
26a305a96b fix(deps): update dependency zod to v3.25.74
All checks were successful
Lint / Lint and Typecheck (push) Successful in 45s
2025-07-05 03:03:24 +00:00
fb32bd381c chore(deps): update dependency typescript-eslint to v8.35.1
All checks were successful
Lint / Lint and Typecheck (push) Successful in 45s
2025-07-05 02:03:37 +00:00
c45cad8e96 chore(deps): update dependency tw-animate-css to v1.3.5
All checks were successful
Lint / Lint and Typecheck (push) Successful in 43s
2025-07-05 01:03:56 +00:00
86ee8c6b32 chore(deps): update dependency eslint to v9.30.1
All checks were successful
Lint / Lint and Typecheck (push) Successful in 45s
2025-07-05 00:02:57 +00:00
f6dd7c3012 chore(deps): update dependency @types/node to v22.15.34
All checks were successful
Lint / Lint and Typecheck (push) Successful in 49s
2025-06-28 08:02:13 +00:00
e34fc5dffc fix(deps): update dependency react-hook-form to v7.59.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 43s
2025-06-28 06:02:57 +00:00
57be648512 fix(deps): update dependency lucide-react to ^0.525.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 51s
2025-06-28 05:03:07 +00:00
c131fba360 chore(deps): update dependency typescript-eslint to v8.35.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 48s
2025-06-28 04:03:27 +00:00
9803c3f33d chore(deps): update dependency prettier to v3.6.2
All checks were successful
Lint / Lint and Typecheck (push) Successful in 42s
2025-06-28 03:03:35 +00:00
7f7a8d8728 chore(deps): update dependency eslint to v9.30.0
All checks were successful
Lint / Lint and Typecheck (push) Successful in 42s
2025-06-28 02:04:13 +00:00
a2184c0ef2 chore(deps): update tailwindcss monorepo to v4.1.11
All checks were successful
Lint / Lint and Typecheck (push) Successful in 42s
2025-06-28 01:04:19 +00:00
198a8c8c45 chore(deps): update dependency @types/node to v22.15.33
All checks were successful
Lint / Lint and Typecheck (push) Successful in 52s
2025-06-28 00:03:05 +00:00
8 changed files with 5359 additions and 7714 deletions

View File

@@ -4,7 +4,7 @@ on:
pull_request:
push:
branches:
- '**' # matches every branch
- "**" # matches every branch
jobs:
lint_and_typecheck:
@@ -15,14 +15,17 @@ jobs:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
- name: Install pnpm
uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4
- name: Setup Node.js
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22
cache: 'npm'
cache: "pnpm"
- name: Install dependencies
run: npm ci
run: pnpm install
- name: Run check
run: npm run check
run: pnpm run check

View File

@@ -29,7 +29,6 @@ The projects code is structured using React/Next.js with TypeScript, focusing
The calculator models your FIRE journey in two phases:
1. **Accumulation:**
- Your starting capital is grown by your expected CAGR (~7% by default).
- Monthly savings are added for each year until retirement.
- Every variable can be adjusted live (capital, savings, age, growth, inflation, spending, target retirement).
@@ -58,11 +57,11 @@ To run locally:
```
2. **Install dependencies**
```bash
npm install
pnpm install
```
3. **Run the app**
```bash
npm run dev
pnpm run dev
```
4. Visit [http://localhost:3000](http://localhost:3000) and unleash the fire.

7659
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
"scripts": {
"build": "next build",
"check": "next lint && tsc --noEmit",
"dev": "next dev --turbo",
"dev": "next dev --turbopack",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mdx}\" --cache",
"format:write": "prettier --write \"**/*.{ts,tsx,js,jsx,mdx}\" --cache",
"lint:fix": "next lint --fix",
@@ -23,34 +23,35 @@
"@t3-oss/env-nextjs": "^0.13.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.522.0",
"next": "^15.2.3",
"lucide-react": "^0.539.0",
"next": "^15.4.1",
"next-plausible": "^3.12.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.56.1",
"recharts": "^3.0.0",
"recharts": "^2.15.3",
"tailwind-merge": "^3.2.0",
"zod": "^3.24.3"
"zod": "^4.0.0"
},
"devDependencies": {
"@eslint/eslintrc": "3.3.1",
"@tailwindcss/postcss": "4.1.10",
"@types/node": "22.15.32",
"@types/react": "19.1.8",
"@types/react-dom": "19.1.6",
"eslint": "9.29.0",
"eslint-config-next": "15.3.4",
"@tailwindcss/postcss": "4.1.11",
"@types/node": "22.17.1",
"@types/react": "19.1.10",
"@types/react-dom": "19.1.7",
"eslint": "9.33.0",
"eslint-config-next": "15.4.4",
"eslint-plugin-react-hooks": "5.2.0",
"postcss": "8.5.6",
"prettier": "3.5.3",
"prettier-plugin-tailwindcss": "0.6.13",
"tailwindcss": "4.1.10",
"tw-animate-css": "1.3.4",
"typescript": "5.8.3",
"typescript-eslint": "8.34.1"
"prettier": "3.6.2",
"prettier-plugin-tailwindcss": "0.6.14",
"tailwindcss": "4.1.11",
"tw-animate-css": "1.3.6",
"typescript": "5.9.2",
"typescript-eslint": "8.39.0"
},
"ct3aMetadata": {
"initVersion": "7.39.3"
},
"packageManager": "npm@11.4.2"
"packageManager": "pnpm@10.14.0"
}

5184
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

6
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,6 @@
ignoredBuiltDependencies:
- unrs-resolver
onlyBuiltDependencies:
- '@tailwindcss/oxide'
- sharp

View File

@@ -120,7 +120,7 @@ export default function FireCalculatorForm() {
const [showing4percent, setShowing4percent] = useState(false);
// Initialize form with default values
const form = useForm<FormValues>({
const form = useForm<z.input<typeof formSchema>, undefined, FormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
startingCapital: 50000,
@@ -258,11 +258,18 @@ export default function FireCalculatorForm() {
<Input
placeholder="e.g., 10000"
type="number"
{...field}
onChange={(value) => {
field.onChange(value);
value={field.value as number | string | undefined}
onChange={(e) => {
field.onChange(
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)();
}}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/>
</FormControl>
<FormMessage />
@@ -279,11 +286,18 @@ export default function FireCalculatorForm() {
<Input
placeholder="e.g., 500"
type="number"
{...field}
onChange={(value) => {
field.onChange(value);
value={field.value as number | string | undefined}
onChange={(e) => {
field.onChange(
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)();
}}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/>
</FormControl>
<FormMessage />
@@ -300,11 +314,18 @@ export default function FireCalculatorForm() {
<Input
placeholder="e.g., 30"
type="number"
{...field}
onChange={(value) => {
field.onChange(value);
value={field.value as number | string | undefined}
onChange={(e) => {
field.onChange(
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)();
}}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/>
</FormControl>
<FormMessage />
@@ -321,11 +342,18 @@ export default function FireCalculatorForm() {
<Input
placeholder="e.g., 90"
type="number"
{...field}
onChange={(value) => {
field.onChange(value);
value={field.value as number | string | undefined}
onChange={(e) => {
field.onChange(
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)();
}}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/>
</FormControl>
<FormMessage />
@@ -343,11 +371,18 @@ export default function FireCalculatorForm() {
placeholder="e.g., 7"
type="number"
step="0.1"
{...field}
onChange={(value) => {
field.onChange(value);
value={field.value as number | string | undefined}
onChange={(e) => {
field.onChange(
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)();
}}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/>
</FormControl>
<FormMessage />
@@ -365,11 +400,18 @@ export default function FireCalculatorForm() {
placeholder="e.g., 2"
type="number"
step="0.1"
{...field}
onChange={(value) => {
field.onChange(value);
value={field.value as number | string | undefined}
onChange={(e) => {
field.onChange(
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)();
}}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/>
</FormControl>
<FormMessage />
@@ -388,11 +430,18 @@ export default function FireCalculatorForm() {
<Input
placeholder="e.g., 2000"
type="number"
{...field}
onChange={(value) => {
field.onChange(value);
value={field.value as number | string | undefined}
onChange={(e) => {
field.onChange(
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)();
}}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/>
</FormControl>
<FormMessage />
@@ -406,11 +455,13 @@ export default function FireCalculatorForm() {
name="retirementAge"
render={({ field }) => (
<FormItem>
<FormLabel>Retirement Age: {field.value}</FormLabel>
<FormLabel>
Retirement Age: {field.value as number}
</FormLabel>
<FormControl>
<Slider
name="retirementAge"
value={[field.value]}
value={[field.value as number]}
min={25}
max={75}
step={1}
@@ -563,8 +614,8 @@ export default function FireCalculatorForm() {
<ReferenceLine
x={
irlYear +
(form.getValues("retirementAge") -
form.getValues("currentAge"))
(Number(form.getValues("retirementAge")) -
Number(form.getValues("currentAge")))
}
stroke="var(--primary)"
strokeWidth={2}
@@ -579,7 +630,7 @@ export default function FireCalculatorForm() {
x={
irlYear +
(result.retirementAge4percent -
form.getValues("currentAge"))
Number(form.getValues("currentAge")))
}
stroke="var(--secondary)"
strokeWidth={1}
@@ -642,8 +693,8 @@ export default function FireCalculatorForm() {
</CardHeader>
<CardContent>
<p className="text-3xl font-bold">
{form.getValues("lifeExpectancy") -
form.getValues("retirementAge")}
{Number(form.getValues("lifeExpectancy")) -
Number(form.getValues("retirementAge"))}
</p>
</CardContent>
</Card>
@@ -674,7 +725,7 @@ export default function FireCalculatorForm() {
</CardHeader>
<CardContent>
<p className="text-3xl font-bold">
{form.getValues("lifeExpectancy") -
{Number(form.getValues("lifeExpectancy")) -
(result.retirementAge4percent ?? 0)}
</p>
</CardContent>

View File

@@ -10,6 +10,61 @@ import Footer from "./components/footer";
import BackgroundPattern from "./components/BackgroundPattern";
export default function HomePage() {
const faqData = {
"@context": "https://schema.org",
"@type": "FAQPage",
mainEntity: [
{
"@type": "Question",
name: "What methodology does this calculator use?",
acceptedAnswer: {
"@type": "Answer",
text: "We run a multi-year projection in two phases: 1. Accumulation: Your balance grows by CAGR and you add monthly savings. 2. Retirement: The balance continues compounding, but you withdraw an inflation-adjusted monthly allowance. The result: a precise estimate of the capital you'll have at retirement (your “FIRE Number”) and how long it will last until your chosen life expectancy.",
},
},
{
"@type": "Question",
name: "Why isn't this just the 4% rule?",
acceptedAnswer: {
"@type": "Answer",
text: "The 4% rule is a useful starting point (25× annual spending), but it assumes a fixed withdrawal rate with inflation adjustments and doesn't model ongoing savings or dynamic market returns. Our calculator simulates each year's growth, contributions, and inflation-indexed withdrawals to give you a tailored picture.",
},
},
{
"@type": "Question",
name: "How do I choose a realistic growth rate?",
acceptedAnswer: {
"@type": "Answer",
text: "Historically, a diversified portfolio of equities and bonds has returned around 7-10% per year before inflation. We recommend starting around 6-8% (net of fees), then running “what-if” scenarios—5% on the conservative side, 10% on the aggressive side—to see how they affect your timeline.",
},
},
{
"@type": "Question",
name: "How does inflation factor into my FIRE Number?",
acceptedAnswer: {
"@type": "Answer",
text: "Cost of living rises. To maintain today's lifestyle, your monthly allowance must grow each year by your inflation rate. This calculator automatically inflates your desired monthly spending and subtracts it from your portfolio during retirement, ensuring your FIRE Number keeps pace with rising expenses.",
},
},
{
"@type": "Question",
name: "Can I really retire early with FIRE?",
acceptedAnswer: {
"@type": "Answer",
text: "Early retirement is achievable with disciplined saving, smart investing, and realistic assumptions. This tool helps you set targets, visualize outcomes, and adjust inputs—so you can build confidence in your plan and make informed trade-offs between lifestyle, risk, and timeline.",
},
},
{
"@type": "Question",
name: "How should I use this calculator effectively?",
acceptedAnswer: {
"@type": "Answer",
text: "Start with your actual numbers (capital, savings, age). Set conservative - mid - aggressive growth rates to bound possibilities. Slide your retirement age to explore “early” vs. “traditional” scenarios. Review the chart—especially the reference lines—to see when you hit FI and how withdrawals impact your balance. Experiment with higher savings rates or lower target spending to accelerate your path.",
},
},
],
};
return (
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-2">
<BackgroundPattern />
@@ -129,9 +184,14 @@ export default function HomePage() {
</section>
<section className="mb-12">
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(faqData) }}
/>
<h2 className="mb-4 text-3xl font-bold">
FIRE & Investing Frequently Asked Questions (FAQ)
</h2>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
<AccordionTrigger className="text-xl font-semibold">