Compare commits

..

26 Commits

Author SHA1 Message Date
541c443efd auto update on value change 2025-05-01 17:24:45 +02:00
0d12ab9a47 break out functions from export 2025-05-01 15:56:01 +02:00
09e9485f2f redesigned algorith, use user specified retirement age 2025-05-01 15:25:22 +02:00
383625aede shadcn slider 2025-05-01 13:57:54 +02:00
4d7a936721 new strategy human algo 2025-04-30 23:17:48 +02:00
886afab1ef styling, graph sizing and number precision 2025-04-30 20:05:38 +02:00
0f6cd57f3d result style 2025-04-30 19:40:53 +02:00
ffb8e8d506 prettier 2025-04-30 19:20:25 +02:00
c3867ccbd4 fix faq chevron 2025-04-30 18:12:23 +02:00
6bc7be6336 add logo 2025-04-30 18:06:14 +02:00
26d2ec68b8 fix lint errors 2025-04-29 22:49:23 +02:00
5e0ff2891a attempt new formula 2025-04-29 20:29:56 +02:00
1a0428a8e0 lets not be so strict 2025-04-29 20:25:49 +02:00
9267018d06 Select 2025-04-29 20:15:54 +02:00
acec849428 tracking + web vitals 2025-04-29 20:09:37 +02:00
1e9f2cbc2d env 2025-04-29 20:09:07 +02:00
9c460bab22 SEO 2025-04-29 19:55:15 +02:00
4e7705ce53 SEO 2025-04-29 19:32:09 +02:00
d5962bbf9e fixes 2025-04-29 19:22:01 +02:00
64669e5f58 FIRE chart 2025-04-29 19:11:09 +02:00
f05f3fe37c new algorithm 2025-04-29 18:45:58 +02:00
896b0bf063 fix and add charts 2025-04-29 18:45:41 +02:00
716bcc6fef SEO 2025-04-29 18:33:19 +02:00
31415c10a2 FIRE calculator 2025-04-29 18:32:26 +02:00
fe03807739 shadcn 2025-04-29 17:46:38 +02:00
30d27a212e initial files 2025-04-29 17:09:04 +02:00
17 changed files with 7590 additions and 5866 deletions

View File

@@ -1,31 +0,0 @@
name: Lint
on:
pull_request:
push:
branches:
- "**" # matches every branch
jobs:
lint_and_typecheck:
name: Lint and Typecheck
runs-on: ubuntu-latest
steps:
- 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: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Run check
run: pnpm run check

109
README.md
View File

@@ -1,108 +1,3 @@
![InvestingFIRE logo](/src/app/apple-icon.png) # fire
# InvestingFIRE 🔥 — The #1 Interactive FIRE Calculator FIRE calculator
**InvestingFIRE** is a responsive web application for calculating your path to Financial Independence and Early Retirement (FIRE). It features a year-by-year projection engine that simulates both accumulation (savings and investment growth) and retirement (withdrawals) phases, allowing users to:
- Input starting capital, monthly savings, expected annual growth rate, inflation rate, current age, desired retirement age, life expectancy, and desired monthly retirement allowance.
- View a dynamic chart displaying projected portfolio balance and monthly allowance over time.
- Instantly see their estimated “FIRE number” (required capital at retirement), how long their capital will last, and compare results to the “4% rule.”
- Adjust assumptions live, with all calculations and visualizations updating automatically.
- Access explanatory content about FIRE methodology, key variables, and additional community resources, all on a single, consolidated page.
The projects code is structured using React/Next.js with TypeScript, focusing on user experience, modern UI components, and clarity of financial assumptions.
---
## 🚀 Features at a Glance
- **⚡️ Real-Time Projections:** Every field updates the chart _as you type_. Experiment with savings, growth rates, inflation, or retirement age and see your future instantly.
- **📈 Interactive Chart:** Dual-area plots for portfolio value and future monthly spending, plus reference lines for FIRE milestones and “4% rule” legends.
- **🧠 Education Baked In:** Contextual tooltips, deep-dive sections on how FIRE works, FAQs, and must-read resources included.
- **🔎 Detailed Methodology:** Not just a 25x rule — runs a full, year-by-year simulation with inflation-adjusted withdrawals and optional 4%-rule overlays.
- **👌 Modern UX:** Typing, sliding, or clicking feels _good_. Responsive on all devices.
---
## 🧰 How It Works
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).
2. **Retirement:**
- Your balance continues to grow by CAGR.
- Each year, an inflation-adjusted monthly allowance is withdrawn.
- The simulation runs until your selected life expectancy, showing the possibility of portfolio depletion.
**Key Outputs:**
- 🔥 “FIRE Number”: Portfolio value at your defined retirement age
- 📊 Interactive projection chart: See how your nest egg and withdrawals evolve over time
- 4⃣ “4% Rule” overlays: Compare dynamic results to classic rule-of-thumb
---
## 🌟 Try It For Yourself
To run locally:
1. **Clone the repo**
```bash
git clone https://git.schulze.network/schulze/fire.git
cd fire
```
2. **Install dependencies**
```bash
pnpm install
```
3. **Run the app**
```bash
pnpm run dev
```
4. Visit [http://localhost:3000](http://localhost:3000) and unleash the fire.
Deployed version: [https://investingfire.com](https://investingfire.com)
---
## ✏️ Inputs & Variables
- **Starting Capital** — How much youve already invested
- **Monthly Savings** — What youll add each month
- **Current Age & Retirement Age** — Your FI timeline
- **Life Expectancy** — How long do you want income to last?
- **Expected Growth Rate (CAGR)** — Portfolio annual % return, before inflation
- **Inflation Rate** — Cost of living increases
- **Desired Monthly Allowance** — Your lifestyle, todays dollars
As you adjust these, all projections update instantly _without needing to hit “Calculate.”_
Try many “what ifs” fast.
---
## 👩‍💻 Contributing
Pull requests are welcome! Open issues for bugs, new features, or debate about safe withdrawal rates and tax assumptions.
---
## 📄 License
[GPL-3.0](./LICENSE)
---
## 🥇 Why Use InvestingFIRE?
- You want the truth — not just a 4% fantasy.
- You want to learn, not just punch in numbers.
- You want clarity, speed, and modern UI.
- You want to show your friends the best FIRE tool on the web.
Enjoy the _rocket ride_ to financial independence.
**InvestingFIRE — Know your number. Change your future.**

7300
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,12 +6,14 @@
"scripts": { "scripts": {
"build": "next build", "build": "next build",
"check": "next lint && tsc --noEmit", "check": "next lint && tsc --noEmit",
"dev": "next dev --turbopack", "dev": "next dev --turbo",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mdx}\" --cache",
"format:write": "prettier --write \"**/*.{ts,tsx,js,jsx,mdx}\" --cache", "format:write": "prettier --write \"**/*.{ts,tsx,js,jsx,mdx}\" --cache",
"lint": "next lint",
"lint:fix": "next lint --fix", "lint:fix": "next lint --fix",
"preview": "next build && next start", "preview": "next build && next start",
"start": "next start" "start": "next start",
"typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@hookform/resolvers": "^5.0.1", "@hookform/resolvers": "^5.0.1",
@@ -20,38 +22,37 @@
"@radix-ui/react-select": "^2.2.2", "@radix-ui/react-select": "^2.2.2",
"@radix-ui/react-slider": "^1.3.2", "@radix-ui/react-slider": "^1.3.2",
"@radix-ui/react-slot": "^1.2.0", "@radix-ui/react-slot": "^1.2.0",
"@t3-oss/env-nextjs": "^0.13.0", "@t3-oss/env-nextjs": "^0.12.0",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.525.0", "lucide-react": "^0.503.0",
"next": "^15.4.1", "next": "^15.2.3",
"next-plausible": "^3.12.4", "next-plausible": "^3.12.4",
"react": "^19.0.0", "react": "^19.0.0",
"react-dom": "^19.0.0", "react-dom": "^19.0.0",
"react-hook-form": "^7.56.1", "react-hook-form": "^7.56.1",
"recharts": "^2.15.3", "recharts": "^2.15.3",
"tailwind-merge": "^3.2.0", "tailwind-merge": "^3.2.0",
"zod": "^4.0.0" "zod": "^3.24.3"
}, },
"devDependencies": { "devDependencies": {
"@eslint/eslintrc": "3.3.1", "@eslint/eslintrc": "^3.3.1",
"@tailwindcss/postcss": "4.1.11", "@tailwindcss/postcss": "^4.0.15",
"@types/node": "22.16.3", "@types/node": "^20.14.10",
"@types/react": "19.1.8", "@types/react": "^19.0.0",
"@types/react-dom": "19.1.6", "@types/react-dom": "^19.0.0",
"eslint": "9.31.0", "eslint": "^9.23.0",
"eslint-config-next": "15.4.1", "eslint-config-next": "^15.2.3",
"eslint-plugin-react-hooks": "5.2.0", "postcss": "^8.5.3",
"postcss": "8.5.6", "prettier": "^3.5.3",
"prettier": "3.6.2", "prettier-plugin-tailwindcss": "^0.6.11",
"prettier-plugin-tailwindcss": "0.6.14", "tailwindcss": "^4.0.15",
"tailwindcss": "4.1.11", "tw-animate-css": "^1.2.8",
"tw-animate-css": "1.3.5", "typescript": "^5.8.2",
"typescript": "5.8.3", "typescript-eslint": "^8.27.0"
"typescript-eslint": "8.36.0"
}, },
"ct3aMetadata": { "ct3aMetadata": {
"initVersion": "7.39.3" "initVersion": "7.39.3"
}, },
"packageManager": "pnpm@10.13.1" "packageManager": "npm@11.2.0"
} }

5055
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1 +0,0 @@
wgu5fuk8d5j5wp3pjtta9vrw8d9by9qk

View File

@@ -1,11 +0,0 @@
{
"extends": ["config:best-practices", ":semanticCommits"],
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true,
"automergeType": "branch"
}
]
}

View File

@@ -1,157 +0,0 @@
"use client";
import { useState, useEffect } from "react";
import {
type LucideIcon,
HandCoins,
Bitcoin,
Coins,
DollarSign,
Euro,
IndianRupee,
JapaneseYen,
PiggyBank,
PoundSterling,
Wallet,
Banknote,
ChartCandlestick,
CirclePercent,
CreditCard,
Gem,
Receipt,
ShoppingBasket,
Rocket,
RockingChair,
Sparkles,
ChartPie,
ChartBar,
BarChart3,
ChartLine,
TrendingDown,
TrendingUp,
Vault,
Landmark,
Briefcase,
Handshake,
Shield,
Lock,
CalendarRange,
Hourglass,
Sprout,
Target,
} from "lucide-react";
export default function MultiIconPattern({ opacity = 0.2, spacing = 160 }) {
const [width, setWidth] = useState(0);
const [height, setHeight] = useState(0);
const [rows, setRows] = useState(0);
const [columns, setColumns] = useState(0);
useEffect(() => {
const updateDimensions = () => {
if (window.innerWidth > width + spacing * 2) {
setWidth(window.innerWidth);
}
if (window.innerHeight > height + spacing * 2) {
setHeight(window.innerHeight);
}
};
updateDimensions();
window.addEventListener("resize", updateDimensions);
return () => {
window.removeEventListener("resize", updateDimensions);
};
}, [height, width, spacing]);
useEffect(() => {
setColumns(Math.ceil(width / spacing) + 3);
}, [width, spacing]);
useEffect(() => {
setRows(Math.ceil(height / spacing) + 3);
}, [height, spacing]);
// Explicitly type the array as LucideIcon[]
const iconComponents: LucideIcon[] = [
HandCoins,
Bitcoin,
Coins,
DollarSign,
Euro,
IndianRupee,
JapaneseYen,
PiggyBank,
PoundSterling,
Wallet,
Banknote,
ChartCandlestick,
CirclePercent,
CreditCard,
Gem,
Receipt,
ShoppingBasket,
Rocket,
RockingChair,
Sparkles,
ChartPie,
ChartBar,
BarChart3,
ChartLine,
TrendingDown,
TrendingUp,
Vault,
Landmark,
Briefcase,
Handshake,
Shield,
Lock,
CalendarRange,
Hourglass,
Sprout,
Target,
];
const renderIcons = ({
rows,
columns,
}: {
rows: number;
columns: number;
}) => {
const icons = [];
for (let y = 0; y < rows; y++) {
for (let x = 0; x < columns; x++) {
// Pick a random icon component from the array
const randomIndex = Math.floor(Math.random() * iconComponents.length);
const IconComponent = iconComponents[randomIndex]!;
// Slightly randomize size and position for more organic feel
const size = 28 + Math.floor(Math.random() * 8);
const xOffset = Math.floor(Math.random() * (spacing / 1.618));
const yOffset = Math.floor(Math.random() * (spacing / 1.618));
icons.push(
<IconComponent
key={`icon-${x}-${y}`}
size={size}
className="text-primary fixed"
style={{
left: `${x * spacing + xOffset}px`,
top: `${y * spacing + yOffset}px`,
opacity: opacity,
transform: `rotate(${Math.round((Math.random() - 0.5) * 30)}deg)`,
}}
/>,
);
}
}
return icons;
};
return (
<div className="absolute h-full w-full">
{width > 0 && renderIcons({ rows, columns })}
</div>
);
}

View File

@@ -73,16 +73,12 @@ interface YearlyData {
age: number; age: number;
year: number; year: number;
balance: number; balance: number;
untouchedBalance: number;
phase: "accumulation" | "retirement"; phase: "accumulation" | "retirement";
monthlyAllowance: number; monthlyAllowance: number;
untouchedMonthlyAllowance: number;
} }
interface CalculationResult { interface CalculationResult {
fireNumber: number | null; fireNumber: number | null;
fireNumber4percent: number | null;
retirementAge4percent: number | null;
yearlyData: YearlyData[]; yearlyData: YearlyData[];
error?: string; error?: string;
} }
@@ -105,8 +101,8 @@ const tooltipRenderer = ({
return ( return (
<div className="bg-background border p-2 shadow-sm"> <div className="bg-background border p-2 shadow-sm">
<p className="font-medium">{`Year: ${data.year.toString()} (Age: ${data.age.toString()})`}</p> <p className="font-medium">{`Year: ${data.year.toString()} (Age: ${data.age.toString()})`}</p>
<p className="text-orange-500">{`Balance: ${formatNumber(data.balance)}`}</p> <p className="text-chart-1">{`Balance: ${formatNumber(data.balance)}`}</p>
<p className="text-red-600">{`Monthly allowance: ${formatNumber(data.monthlyAllowance)}`}</p> <p className="text-chart-2">{`Monthly allowance: ${formatNumber(data.monthlyAllowance)}`}</p>
<p>{`Phase: ${data.phase === "accumulation" ? "Accumulation" : "Retirement"}`}</p> <p>{`Phase: ${data.phase === "accumulation" ? "Accumulation" : "Retirement"}`}</p>
</div> </div>
); );
@@ -117,10 +113,9 @@ const tooltipRenderer = ({
export default function FireCalculatorForm() { export default function FireCalculatorForm() {
const [result, setResult] = useState<CalculationResult | null>(null); const [result, setResult] = useState<CalculationResult | null>(null);
const irlYear = new Date().getFullYear(); const irlYear = new Date().getFullYear();
const [showing4percent, setShowing4percent] = useState(false);
// Initialize form with default values // Initialize form with default values
const form = useForm<z.input<typeof formSchema>, undefined, FormValues>({ const form = useForm<FormValues>({
resolver: zodResolver(formSchema), resolver: zodResolver(formSchema),
defaultValues: { defaultValues: {
startingCapital: 50000, startingCapital: 50000,
@@ -128,7 +123,7 @@ export default function FireCalculatorForm() {
currentAge: 25, currentAge: 25,
cagr: 7, cagr: 7,
desiredMonthlyAllowance: 3000, desiredMonthlyAllowance: 3000,
inflationRate: 2.3, inflationRate: 2,
lifeExpectancy: 84, lifeExpectancy: 84,
retirementAge: 55, retirementAge: 55,
}, },
@@ -154,10 +149,8 @@ export default function FireCalculatorForm() {
age: age, age: age,
year: irlYear, year: irlYear,
balance: startingCapital, balance: startingCapital,
untouchedBalance: startingCapital,
phase: "accumulation", phase: "accumulation",
monthlyAllowance: 0, monthlyAllowance: initialMonthlyAllowance,
untouchedMonthlyAllowance: initialMonthlyAllowance,
}); });
// Calculate accumulation phase (before retirement) // Calculate accumulation phase (before retirement)
@@ -182,18 +175,13 @@ export default function FireCalculatorForm() {
newBalance = newBalance =
previousYearData.balance * annualGrowthRate - inflatedAllowance * 12; previousYearData.balance * annualGrowthRate - inflatedAllowance * 12;
} }
const untouchedBalance =
previousYearData.untouchedBalance * annualGrowthRate +
monthlySavings * 12;
const allowance = phase === "retirement" ? inflatedAllowance : 0;
yearlyData.push({ yearlyData.push({
age: currentAge, age: currentAge,
year: year, year: year,
balance: newBalance, balance: newBalance,
untouchedBalance: untouchedBalance,
phase: phase, phase: phase,
monthlyAllowance: allowance, monthlyAllowance: inflatedAllowance,
untouchedMonthlyAllowance: inflatedAllowance,
}); });
} }
@@ -204,23 +192,9 @@ export default function FireCalculatorForm() {
); );
const retirementData = yearlyData[retirementIndex]; const retirementData = yearlyData[retirementIndex];
const [fireNumber4percent, retirementAge4percent] = (() => {
for (const yearData of yearlyData) {
if (
yearData.untouchedBalance >
(yearData.untouchedMonthlyAllowance * 12) / 0.04
) {
return [yearData.untouchedBalance, yearData.age];
}
}
return [0, 0];
})();
if (retirementIndex === -1 || !retirementData) { if (retirementIndex === -1 || !retirementData) {
setResult({ setResult({
fireNumber: null, fireNumber: null,
fireNumber4percent: null,
retirementAge4percent: null,
error: "Could not calculate retirement data", error: "Could not calculate retirement data",
yearlyData: yearlyData, yearlyData: yearlyData,
}); });
@@ -228,8 +202,6 @@ export default function FireCalculatorForm() {
// Set the result // Set the result
setResult({ setResult({
fireNumber: retirementData.balance, fireNumber: retirementData.balance,
fireNumber4percent: fireNumber4percent,
retirementAge4percent: retirementAge4percent,
yearlyData: yearlyData, yearlyData: yearlyData,
}); });
} }
@@ -258,18 +230,11 @@ export default function FireCalculatorForm() {
<Input <Input
placeholder="e.g., 10000" placeholder="e.g., 10000"
type="number" type="number"
value={field.value as number | string | undefined} {...field}
onChange={(e) => { onChange={(value) => {
field.onChange( field.onChange(value);
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -286,18 +251,11 @@ export default function FireCalculatorForm() {
<Input <Input
placeholder="e.g., 500" placeholder="e.g., 500"
type="number" type="number"
value={field.value as number | string | undefined} {...field}
onChange={(e) => { onChange={(value) => {
field.onChange( field.onChange(value);
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -314,18 +272,11 @@ export default function FireCalculatorForm() {
<Input <Input
placeholder="e.g., 30" placeholder="e.g., 30"
type="number" type="number"
value={field.value as number | string | undefined} {...field}
onChange={(e) => { onChange={(value) => {
field.onChange( field.onChange(value);
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -342,18 +293,11 @@ export default function FireCalculatorForm() {
<Input <Input
placeholder="e.g., 90" placeholder="e.g., 90"
type="number" type="number"
value={field.value as number | string | undefined} {...field}
onChange={(e) => { onChange={(value) => {
field.onChange( field.onChange(value);
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -371,18 +315,11 @@ export default function FireCalculatorForm() {
placeholder="e.g., 7" placeholder="e.g., 7"
type="number" type="number"
step="0.1" step="0.1"
value={field.value as number | string | undefined} {...field}
onChange={(e) => { onChange={(value) => {
field.onChange( field.onChange(value);
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -400,18 +337,11 @@ export default function FireCalculatorForm() {
placeholder="e.g., 2" placeholder="e.g., 2"
type="number" type="number"
step="0.1" step="0.1"
value={field.value as number | string | undefined} {...field}
onChange={(e) => { onChange={(value) => {
field.onChange( field.onChange(value);
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -430,18 +360,11 @@ export default function FireCalculatorForm() {
<Input <Input
placeholder="e.g., 2000" placeholder="e.g., 2000"
type="number" type="number"
value={field.value as number | string | undefined} {...field}
onChange={(e) => { onChange={(value) => {
field.onChange( field.onChange(value);
e.target.value === ""
? undefined
: Number(e.target.value),
);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
onBlur={field.onBlur}
name={field.name}
ref={field.ref}
/> />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
@@ -455,18 +378,16 @@ export default function FireCalculatorForm() {
name="retirementAge" name="retirementAge"
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormLabel> <FormLabel>Retirement Age: {field.value}</FormLabel>
Retirement Age: {field.value as number}
</FormLabel>
<FormControl> <FormControl>
<Slider <Slider
name="retirementAge" name="retirementAge"
value={[field.value as number]} value={[field.value]}
min={25} min={18}
max={75} max={form.getValues("lifeExpectancy")}
step={1} step={1}
onValueChange={(value: number[]) => { onValueChange={(value) => {
field.onChange(value[0]); field.onChange(...value);
void form.handleSubmit(onSubmit)(); void form.handleSubmit(onSubmit)();
}} }}
className="py-4" className="py-4"
@@ -491,14 +412,23 @@ export default function FireCalculatorForm() {
Projected balance growth with your selected retirement age Projected balance growth with your selected retirement age
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="px-2"> <CardContent>
<ChartContainer <ChartContainer
className="aspect-auto h-80 w-full" className="aspect-auto h-80 w-full"
config={{}} config={{
balance: {
label: "Balance",
color: "var(--chart-1)",
},
realBalance: {
label: "Real Balance",
color: "var(--chart-3)",
},
}}
> >
<AreaChart <AreaChart
data={result.yearlyData} data={result.yearlyData}
margin={{ top: 10, right: 20, left: 20, bottom: 10 }} margin={{ top: 20, right: 30, left: 20, bottom: 20 }}
> >
<CartesianGrid strokeDasharray="3 3" /> <CartesianGrid strokeDasharray="3 3" />
<XAxis <XAxis
@@ -509,10 +439,7 @@ export default function FireCalculatorForm() {
offset: -10, offset: -10,
}} }}
/> />
{/* Right Y axis */}
<YAxis <YAxis
yAxisId={"right"}
orientation="right"
tickFormatter={(value: number) => { tickFormatter={(value: number) => {
if (value >= 1000000) { if (value >= 1000000) {
return `${(value / 1000000).toPrecision(3)}M`; return `${(value / 1000000).toPrecision(3)}M`;
@@ -525,24 +452,7 @@ export default function FireCalculatorForm() {
} }
return value.toString(); return value.toString();
}} }}
width={30} width={25}
stroke="var(--color-orange-500)"
tick={{}}
/>
{/* Left Y axis */}
<YAxis
yAxisId="left"
orientation="left"
tickFormatter={(value: number) => {
if (value >= 1000000) {
return `${(value / 1000000).toPrecision(3)}M`;
} else if (value >= 1000) {
return `${(value / 1000).toPrecision(3)}K`;
}
return value.toString();
}}
width={30}
stroke="var(--color-red-600)"
/> />
<ChartTooltip content={tooltipRenderer} /> <ChartTooltip content={tooltipRenderer} />
<defs> <defs>
@@ -555,12 +465,30 @@ export default function FireCalculatorForm() {
> >
<stop <stop
offset="5%" offset="5%"
stopColor="var(--color-orange-500)" stopColor="var(--chart-1)"
stopOpacity={0.8} stopOpacity={0.8}
/> />
<stop <stop
offset="95%" offset="95%"
stopColor="var(--color-orange-500)" stopColor="var(--chart-1)"
stopOpacity={0.1}
/>
</linearGradient>
<linearGradient
id="fillAllowance"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor="var(--chart-2)"
stopOpacity={0.8}
/>
<stop
offset="95%"
stopColor="var(--chart-2)"
stopOpacity={0.1} stopOpacity={0.1}
/> />
</linearGradient> </linearGradient>
@@ -569,92 +497,50 @@ export default function FireCalculatorForm() {
type="monotone" type="monotone"
dataKey="balance" dataKey="balance"
name="balance" name="balance"
stroke="var(--color-orange-500)" stroke="var(--chart-1)"
fill="url(#fillBalance)" fill="url(#fillBalance)"
fillOpacity={0.9} fillOpacity={0.4}
activeDot={{ r: 6 }} activeDot={{ r: 6 }}
yAxisId={"right"}
stackId={"a"}
/> />
<Area <Area
type="step" type="monotone"
dataKey="monthlyAllowance" dataKey="monthlyAllowance"
name="allowance" name="allowance"
stroke="var(--color-red-600)" stroke="var(--chart-2)"
fill="none" fill="url(#fillAllowance)"
fillOpacity={0.4}
activeDot={{ r: 6 }} activeDot={{ r: 6 }}
yAxisId="left"
/> />
{result.fireNumber && ( {result.fireNumber && (
<ReferenceLine <ReferenceLine
y={result.fireNumber} y={result.fireNumber}
stroke="var(--primary)" stroke="var(--chart-3)"
strokeWidth={2} strokeWidth={1}
strokeDasharray="2 1" strokeDasharray="2 2"
label={{ label={{
value: "FIRE Number", value: "FIRE Number",
position: "insideBottomRight", position: "insideBottomRight",
}} }}
yAxisId={"right"}
/>
)}
{result.fireNumber4percent && showing4percent && (
<ReferenceLine
y={result.fireNumber4percent}
stroke="var(--secondary)"
strokeWidth={1}
strokeDasharray="1 1"
label={{
value: "4%-Rule FIRE Number",
position: "insideBottomLeft",
}}
yAxisId={"right"}
/> />
)} )}
<ReferenceLine <ReferenceLine
x={ x={
irlYear + irlYear +
(Number(form.getValues("retirementAge")) - (form.getValues("retirementAge") -
Number(form.getValues("currentAge"))) form.getValues("currentAge"))
} }
stroke="var(--primary)" stroke="var(--chart-2)"
strokeWidth={2} strokeWidth={2}
label={{ label={{
value: "Retirement", value: "Retirement",
position: "insideTopRight", position: "insideTopRight",
}} }}
yAxisId={"left"}
/> />
{result.retirementAge4percent && showing4percent && (
<ReferenceLine
x={
irlYear +
(result.retirementAge4percent -
Number(form.getValues("currentAge")))
}
stroke="var(--secondary)"
strokeWidth={1}
label={{
value: "4%-Rule Retirement",
position: "insideBottomLeft",
}}
yAxisId={"left"}
/>
)}
</AreaChart> </AreaChart>
</ChartContainer> </ChartContainer>
</CardContent> </CardContent>
</Card> </Card>
)} )}
{result && (
<Button
onClick={() => setShowing4percent(!showing4percent)}
variant={showing4percent ? "secondary" : "default"}
size={"sm"}
>
{showing4percent ? "Hide" : "Show"} 4%-Rule
</Button>
)}
</form> </form>
</Form> </Form>
</CardContent> </CardContent>
@@ -693,45 +579,11 @@ export default function FireCalculatorForm() {
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<p className="text-3xl font-bold"> <p className="text-3xl font-bold">
{Number(form.getValues("lifeExpectancy")) - {form.getValues("lifeExpectancy") -
Number(form.getValues("retirementAge"))} form.getValues("retirementAge")}
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
{showing4percent && (
<>
<Card>
<CardHeader>
<CardTitle>4%-Rule FIRE Number</CardTitle>
<CardDescription className="text-xs">
Capital needed for 4% of it to be greater than your
yearly allowance
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-3xl font-bold">
{formatNumber(result.fireNumber4percent)}
</p>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>4%-Rule Retirement Duration</CardTitle>
<CardDescription className="text-xs">
Years to enjoy your financial independence if you follow
the 4% rule
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-3xl font-bold">
{Number(form.getValues("lifeExpectancy")) -
(result.retirementAge4percent ?? 0)}
</p>
</CardContent>
</Card>
</>
)}
</> </>
)} )}
</div> </div>

View File

@@ -1,16 +0,0 @@
export default function Footer() {
return (
<footer className="w-full py-8 text-center text-xs">
<p className="text-xs">
© {new Date().getFullYear()} InvestingFIRE. All rights reserved.{" "}
<a
href="https://schulze.network"
target="_blank"
className="text-primary hover:underline"
>
Hosting by Schulze.network
</a>
</p>
</footer>
);
}

BIN
src/app/favicon.ico (Stored with Git LFS)

Binary file not shown.

View File

@@ -1,17 +1,15 @@
import "@/styles/globals.css"; import "@/styles/globals.css";
import PlausibleProvider from "next-plausible"; import PlausibleProvider from "next-plausible";
import { type Metadata, type Viewport } from "next"; import { type Metadata } from "next";
import { Geist } from "next/font/google"; import { Geist } from "next/font/google";
import { WebVitals } from "./components/web-vitals"; import { WebVitals } from "./components/web-vitals";
export const viewport: Viewport = {
themeColor: [{ color: "oklch(0.97 0.0228 95.96)" }],
};
export const metadata: Metadata = { export const metadata: Metadata = {
title: "InvestingFIRE | Finance and Retirement Calculator", title:
"InvestingFIRE Calculator | Plan Your Financial Independence & Early Retirement",
description: description:
"Achieve Financial Independence & Early Retirement! Plan your FIRE journey with the InvestingFIRE calculator and get personalized projections in buttersmooth graphs.", "Achieve Financial Independence, Retire Early (FIRE) with the InvestingFIRE calculator. Get personalized projections and investing advice to plan your journey.",
icons: [{ rel: "icon", url: "/favicon.ico" }],
}; };
const geist = Geist({ const geist = Geist({
@@ -26,16 +24,17 @@ export default function RootLayout({
<html lang="en" className={geist.variable}> <html lang="en" className={geist.variable}>
<head> <head>
<meta name="apple-mobile-web-app-title" content="FIRE" /> <meta name="apple-mobile-web-app-title" content="FIRE" />
</head>
<PlausibleProvider <PlausibleProvider
domain="investingfire.com" domain="investingfire.com"
customDomain="https://analytics.schulze.network" customDomain="https://analytics.schulze.network"
selfHosted={true} selfHosted={true}
enabled={true} enabled={true}
trackOutboundLinks={true} trackOutboundLinks={true}
/> >
</head>
<WebVitals /> <WebVitals />
<body>{children}</body> <body>{children}</body>
</PlausibleProvider>
</html> </html>
); );
} }

View File

@@ -6,18 +6,13 @@ import {
AccordionItem, AccordionItem,
AccordionTrigger, AccordionTrigger,
} from "@/components/ui/accordion"; } from "@/components/ui/accordion";
import Footer from "./components/footer";
import BackgroundPattern from "./components/BackgroundPattern";
export default function HomePage() { export default function HomePage() {
return ( return (
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-2"> <main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-4">
<BackgroundPattern /> <div className="mx-auto flex flex-col items-center justify-center gap-4 text-center">
<div className="z-10 mx-auto flex flex-col items-center justify-center gap-4 text-center"> <div className="flex flex-row flex-wrap items-center justify-center gap-4 align-middle">
<div className="mt-8 flex flex-row flex-wrap items-center justify-center gap-4 align-middle">
<Image <Image
priority
unoptimized
src="/investingfire_logo_no-bg.svg" src="/investingfire_logo_no-bg.svg"
alt="InvestingFIRE Logo" alt="InvestingFIRE Logo"
width={100} width={100}
@@ -36,35 +31,29 @@ export default function HomePage() {
</div> </div>
{/* Added SEO Content Sections */} {/* Added SEO Content Sections */}
<div className="z-10 mx-auto max-w-2xl py-12 text-left"> <div className="mx-auto max-w-2xl py-12 text-left">
<section className="mb-12"> <section className="mb-12">
<h2 className="mb-4 text-3xl font-bold"> <h2 className="mb-4 text-3xl font-bold">
What Is FIRE? Understanding Financial Independence and Early What is FIRE? Understanding Financial Independence and Early
Retirement Retirement
</h2> </h2>
<p className="mb-4 text-lg leading-relaxed"> <p className="mb-4 text-lg leading-relaxed">
FIRE stands for{" "} FIRE stands for &quot;Financial Independence, Retire Early.&quot;
<strong>Financial Independence, Retire Early</strong>. It&apos;s a It&apos;s a movement focused on aggressive saving and strategic
lifestyle movement built around two core ideas: investing to build a substantial portfolio. The goal is for
investment returns to cover living expenses indefinitely, freeing
you from traditional employment. Achieving FIRE means gaining the
freedom to pursue passions, travel, or simply enjoy life without
needing a regular paycheck. Sound investing advice is crucial for
building the wealth needed.
</p> </p>
<ul className="mb-4 ml-6 list-disc space-y-2 text-lg">
<li>
<strong>Aggressive saving & investing</strong>often 50%+ of
incomeso your capital grows rapidly.
</li>
<li>
<strong>Passive-income coverage</strong>when your investment
returns exceed your living expenses, you gain freedom from a
traditional 9-5.
</li>
</ul>
<p className="text-lg leading-relaxed"> <p className="text-lg leading-relaxed">
By reaching your personal <em>FIRE Number</em>the nest egg needed The core principle involves maximizing your savings rate (often
to cover your inflation-adjusted spendingyou unlock the option to 50%+) and investing wisely, typically in low-cost, diversified
step away from a daily paycheck and pursue passion projects, travel, assets like index funds. Your &quot;FIRE number&quot; the capital
family, or anything else. This calculator helps you simulate your needed is often estimated as 25 times your desired annual
journey, estimate how much you need, and visualize both your spending, derived from the 4% safe withdrawal rate guideline. This
accumulation phase and your retirement withdrawals over time. FIRE calculator helps you personalize this estimate.
</p> </p>
</section> </section>
@@ -73,58 +62,51 @@ export default function HomePage() {
How This FIRE Calculator Provides Investing Insights How This FIRE Calculator Provides Investing Insights
</h2> </h2>
<p className="mb-4 text-lg leading-relaxed"> <p className="mb-4 text-lg leading-relaxed">
Our interactive tool goes beyond a simple 25x annual spending This calculator helps visualize your path to FIRE by projecting
rule. It runs a <strong>year-by-year simulation</strong> of your investment growth based on your inputs. Understanding these
portfolio, combining: projections is a key piece of investing advice for long-term
planning. Here&apos;s a breakdown of the inputs:
</p> </p>
<ul className="mb-4 ml-6 list-disc space-y-2 text-lg"> <ul className="mb-4 ml-6 list-disc space-y-2 text-lg">
<li> <li>
<strong>Starting Capital</strong>your current invested balance <strong>Starting Capital:</strong> The total amount you currently
have invested.
</li> </li>
<li> <li>
<strong>Monthly Savings</strong>ongoing contributions to your <strong>Monthly Savings:</strong> The amount you consistently save
portfolio and invest each month.
</li> </li>
<li> <li>
<strong>Expected Annual Growth Rate (CAGR)</strong>compounding <strong>Current Age:</strong> Your current age in years.
returns before inflation
</li> </li>
<li> <li>
<strong>Annual Inflation Rate</strong>to inflate your target <strong>Expected Annual Growth Rate (%):</strong> The average
withdrawal each year annual return you expect from your investments (after fees, before
inflation).
</li> </li>
<li> <li>
<strong>Desired Monthly Allowance</strong>today&apos;s-value <strong>Desired Monthly Allowance (Today&apos;s Value):</strong>{" "}
spending goal How much you want to be able to spend each month in retirement, in
today&apos;s money value.
</li> </li>
<li> <li>
<strong>Retirement Age & Life Expectancy</strong>defines your <strong>Annual Inflation Rate (%):</strong> The expected average
accumulation horizon and payout period rate at which the cost of living will increase.
</li>
</ul>
<p className="text-lg leading-relaxed">Key features:</p>
<ul className="mb-4 ml-6 list-disc space-y-2 text-lg">
<li>
<strong>Real-time calculation</strong>as you tweak any input,
your FIRE Number and chart update instantly.
</li> </li>
<li> <li>
<strong>Interactive chart</strong> with area plots for both{" "} <strong>Life Expectancy (Age):</strong> The age until which you
<em>portfolio balance</em> and{" "} want your funds to last.
<em>inflation-adjusted allowance</em>, plus reference lines
showing your retirement date and required FIRE Number.
</li>
<li>
<strong>Custom simulation</strong>switches from accumulation
(adding savings) to retirement (withdrawing allowance),
compounding each year based on your growth rate.
</li> </li>
</ul> </ul>
<p className="text-lg leading-relaxed"> <p className="text-lg leading-relaxed">
With this level of granularity, you can confidently experiment with The calculator simulates your investment growth year by year,
savings rate, target retirement age, and investment assumptions to incorporating monthly contributions, the power of compound growth (a
discover how small tweaks speed up or delay your path to financial core investing principle), and inflation&apos;s impact on your
independence. target allowance. It estimates the age at which your capital could
sustain your desired, inflation-adjusted monthly spending throughout
your expected retirement until your specified life expectancy. It
calculates your potential &quot;FIRE Number&quot; and the age you
might reach financial independence.
</p> </p>
</section> </section>
@@ -135,106 +117,86 @@ export default function HomePage() {
<Accordion type="single" collapsible className="w-full"> <Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1"> <AccordionItem value="item-1">
<AccordionTrigger className="text-xl font-semibold"> <AccordionTrigger className="text-xl font-semibold">
What methodology does this calculator use? What is the 4% rule?
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed"> <AccordionContent className="text-lg leading-relaxed">
We run a multi-year projection in two phases: The 4% rule is a guideline suggesting that you can safely
<ol className="ml-6 list-decimal space-y-1"> withdraw 4% of your investment portfolio&apos;s value in your
<li> first year of retirement, and then adjust that amount for
<strong>Accumulation:</strong> Your balance grows by CAGR inflation each subsequent year, with a high probability of your
and you add monthly savings. money lasting for at least 30 years. This calculator uses a more
</li> dynamic simulation based on your life expectancy but is related
<li> to this concept.
<strong>Retirement:</strong> The balance continues
compounding, but you withdraw an inflation-adjusted monthly
allowance.
</li>
</ol>
The result: a precise estimate of the capital you&apos;ll have
at retirement (your FIRE Number) and how long it will last
until your chosen life expectancy.
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
<AccordionItem value="item-2"> <AccordionItem value="item-2">
<AccordionTrigger className="text-xl font-semibold"> <AccordionTrigger className="text-xl font-semibold">
Why isn&apos;t this just the 4% rule? Is the Expected Growth Rate realistic? Finding the right
investing advice often starts here.
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed"> <AccordionContent className="text-lg leading-relaxed">
The 4% rule is a useful starting point (25× annual spending), Historically, diversified stock market investments have returned
but it assumes a fixed withdrawal rate with inflation around 7-10% annually long-term (before inflation). A rate of 7%
adjustments and doesn&apos;t model ongoing savings or dynamic (after fees) is common, but remember past performance
market returns. Our calculator simulates each year&apos;s doesn&apos;t guarantee future results, a fundamental piece of
growth, contributions, and inflation-indexed withdrawals to give investing advice. Choose a rate reflecting your risk tolerance
you a tailored picture. and investment strategy.
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
<AccordionItem value="item-3"> <AccordionItem value="item-3">
<AccordionTrigger className="text-xl font-semibold"> <AccordionTrigger className="text-xl font-semibold">
How do I choose a realistic growth rate? How does inflation impact my FIRE number?
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed"> <AccordionContent className="text-lg leading-relaxed">
Historically, a diversified portfolio of equities and bonds has Inflation erodes the purchasing power of money over time. Your
returned around 7-10% per year before inflation. We recommend desired monthly allowance needs to increase each year just to
starting around 6-8% (net of fees), then running what-if maintain the same standard of living. This calculator accounts
scenarios5% on the conservative side, 10% on the aggressive for this by adjusting your target allowance upwards based on the
sideto see how they affect your timeline. inflation rate you provide, ensuring the calculated FIRE number
supports your desired lifestyle in future dollars.
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
<AccordionItem value="item-4"> <AccordionItem value="item-4">
<AccordionTrigger className="text-xl font-semibold"> <AccordionTrigger className="text-xl font-semibold">
How does inflation factor into my FIRE Number? Can I really retire early with FIRE?
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed"> <AccordionContent className="text-lg leading-relaxed">
Cost of living rises. To maintain today&apos;s lifestyle, your Retiring significantly early is achievable but demands
monthly allowance must grow each year by your inflation rate. discipline, a high savings rate, and smart investing. Success
This calculator automatically inflates your desired monthly depends on income, expenses, savings habits, and investment
spending and subtracts it from your portfolio during retirement, returns. Use this FIRE calculator as a planning tool,
ensuring your FIRE Number keeps pace with rising expenses. understanding it provides estimates based on your assumptions
and chosen investing approach.
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
<AccordionItem value="item-5"> <AccordionItem value="item-5">
<AccordionTrigger className="text-xl font-semibold"> <AccordionTrigger className="text-xl font-semibold">
Can I really retire early with FIRE? What does FIRE stand for?
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed"> <AccordionContent className="text-lg leading-relaxed">
Early retirement is achievable with disciplined saving, smart FIRE stands for Financial Independence, Retire Early. It
investing, and realistic assumptions. This tool helps you set represents a lifestyle movement aimed at maximizing your savings
targets, visualize outcomes, and adjust inputsso you can build rate through increased income and/or decreased expenses to
confidence in your plan and make informed trade-offs between achieve financial independence and retire much earlier than
lifestyle, risk, and timeline. traditional retirement age.
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
<AccordionItem value="item-6"> <AccordionItem value="item-6">
<AccordionTrigger className="text-xl font-semibold"> <AccordionTrigger className="text-xl font-semibold">
How should I use this calculator effectively? How much should I save each month?
</AccordionTrigger> </AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed"> <AccordionContent className="text-lg leading-relaxed">
<ul className="ml-6 list-disc space-y-1"> FIRE enthusiasts typically aim to save 50-70% of their income.
<li> The more you can save, the faster you&apos;ll reach your FIRE
Start with your actual numbers (capital, savings, age). goal. However, the right amount depends on your income,
</li> lifestyle, and target retirement age. Use the calculator to
<li> experiment with different monthly savings amounts to see their
Set conservative - mid - aggressive growth rates to bound impact on your retirement timeline.
possibilities.
</li>
<li>
Slide your retirement age to explore early vs.
traditional scenarios.
</li>
<li>
Review the chartespecially the reference linesto see when
you hit FI and how withdrawals impact your balance.
</li>
<li>
Experiment with higher savings rates or lower target
spending to accelerate your path.
</li>
</ul>
</AccordionContent> </AccordionContent>
</AccordionItem> </AccordionItem>
</Accordion> </Accordion>
@@ -246,58 +208,50 @@ export default function HomePage() {
FIRE Journey & Investing Resources FIRE Journey & Investing Resources
</h2> </h2>
<p className="mb-6 text-lg leading-relaxed"> <p className="mb-6 text-lg leading-relaxed">
Ready to deepen your knowledge and build a bullet-proof plan? Below Ready to dive deeper into FIRE and solidify your investing strategy?
are some of our favorite blogs, books, tools, and communities for Explore these valuable resources for financial independence planning
financial independence and smart investing. and investing advice:
</p> </p>
<div className="bg-foreground my-8 rounded-md p-4 text-lg"> <div className="bg-secondary/20 my-8 rounded-md p-4 text-lg">
<p className="font-semibold">Getting Started with FIRE:</p> <p className="font-semibold">Getting Started with FIRE:</p>
<ol className="ml-6 list-decimal space-y-1"> <ol className="ml-6 list-decimal space-y-1">
<li> <li>
Run your first projection above to find your target FIRE Number. Calculate your personal numbers using this FIRE calculator and
</li> other tools.
<li>Identify areas to boost savings or reduce expenses.</li>
<li>
Study index-fund strategies and low-cost investing advice.
</li> </li>
<li> <li>
Join{" "} Seek sound investing advice and consider joining communities
<a like r/Fire for support.
href="https://www.reddit.com/r/Fire/"
target="_blank"
className="text-primary hover:underline"
>
supportive communities like r/Fire
</a>{" "}
to learn from real journeys.
</li> </li>
<li>Explore books and podcasts to deepen your understanding</li>
</ol> </ol>
</div> </div>
<div className="grid gap-8 md:grid-cols-2"> <div className="grid gap-8 md:grid-cols-2">
<div> <div>
<h3 className="mb-3 text-xl font-semibold">Blogs & Websites</h3> <h3 className="mb-3 text-xl font-semibold">
Blogs & Investing Websites
</h3>
<ul className="ml-6 list-disc space-y-2 text-lg"> <ul className="ml-6 list-disc space-y-2 text-lg">
<li> <li>
<a <a
href="https://www.mrmoneymustache.com/" href="https://www.mrmoneymustache.com/2012/01/13/the-shockingly-simple-math-behind-early-retirement/"
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
Mr. Money Mustache Mr. Money Mustache - Simple Math Behind Early Retirement &
</a>{" "} Investing
- Hardcore frugality & early retirement success stories. </a>
</li> </li>
<li> <li>
<a <a
href="https://www.playingwithfire.co/" href="https://www.playingwithfire.co/resources"
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
Playing With FIRE Playing With FIRE - Comprehensive Resources
</a>{" "} </a>
- Community resources & real-life case studies.
</li> </li>
<li> <li>
<a <a
@@ -305,15 +259,16 @@ export default function HomePage() {
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
r/Fire r/Fire Reddit Community
</a>{" "} </a>
- Active forum for questions, tips, and support.
</li> </li>
</ul> </ul>
</div> </div>
<div> <div>
<h3 className="mb-3 text-xl font-semibold">Books & Podcasts</h3> <h3 className="mb-3 text-xl font-semibold">
Books & Investment Learning
</h3>
<ul className="ml-6 list-disc space-y-2 text-lg"> <ul className="ml-6 list-disc space-y-2 text-lg">
<li> <li>
<a <a
@@ -321,19 +276,18 @@ export default function HomePage() {
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
Your Money or Your Life Your Money or Your Life - Foundational FIRE & Investing
</a>{" "} Principles
- The classic guide to aligning money with values. </a>
</li> </li>
<li> <li>
<a <a
href="https://podcasts.apple.com/us/podcast/biggerpockets-money-podcast/id1330225136" href="https://www.playingwithfire.co/"
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
BiggerPockets Money Podcast Playing With FIRE Documentary
</a>{" "} </a>
- Interviews on FIRE strategies and wealth building.
</li> </li>
<li> <li>
<a <a
@@ -341,39 +295,37 @@ export default function HomePage() {
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
InvestingFIRE Calculator Demo BiggerPockets Money Podcast - FIRE Calculators & Investing
</a>{" "} Strategies
- Deep dive on how interactive projections can guide your </a>
plan.
</li> </li>
</ul> </ul>
</div> </div>
<div> <div>
<h3 className="mb-3 text-xl font-semibold"> <h3 className="mb-3 text-xl font-semibold">
Additional Calculators & Tools Additional FIRE & Investing Calculators
</h3> </h3>
<ul className="ml-6 list-disc space-y-2 text-lg"> <ul className="ml-6 list-disc space-y-2 text-lg">
<li>
<a
href="https://ghostfol.io"
target="_blank"
className="text-primary hover:underline"
>
Ghostfolio
</a>{" "}
- Wealth management application for individuals.
</li>
<li> <li>
<a <a
href="https://walletburst.com/tools/coast-fire-calculator/" href="https://walletburst.com/tools/coast-fire-calculator/"
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
Coast FIRE Calculator Coast FIRE Calculator - For those considering a partial
</a>{" "} early retirement
- When you max out early contributions but let compounding </a>
do the rest. </li>
<li>
<a
href="https://www.empower.com/retirement-calculator"
target="_blank"
className="text-primary hover:underline"
>
Empower Retirement Planner - Free portfolio analysis and net
worth tracking
</a>
</li> </li>
<li> <li>
<a <a
@@ -381,16 +333,43 @@ export default function HomePage() {
target="_blank" target="_blank"
className="text-primary hover:underline" className="text-primary hover:underline"
> >
Compound Interest Calculator CAGR Compound Interest Calculator - Understand Investment
</a>{" "} Growth
- Explore the power of growth rates in isolation. </a>
</li>
</ul>
</div>
<div>
<h3 className="mb-3 text-xl font-semibold">
Recent Investing & FIRE Articles
</h3>
<ul className="ml-6 list-disc space-y-2 text-lg">
<li>
<a
href="https://www.businessinsider.com/retiring-tech-early-coast-fire-make-me-millionaire-2025-4"
target="_blank"
className="text-primary hover:underline"
>
Coast FIRE: Retiring in your 30s while becoming a
millionaire by 60
</a>
</li>
<li>
<a
href="https://www.businessinsider.com/financial-independence-retire-early-saving-loneliness-retreat-bali-making-friends-2025-2"
target="_blank"
className="text-primary hover:underline"
>
The Social Side of FIRE: Finding Community in Financial
Independence
</a>
</li> </li>
</ul> </ul>
</div> </div>
</div> </div>
</section> </section>
</div> </div>
<Footer />
</main> </main>
); );
} }

View File

@@ -1,11 +0,0 @@
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: {
userAgent: "*",
allow: "/",
},
sitemap: "https://investingfire.com/sitemap.xml",
};
}

View File

@@ -1,13 +0,0 @@
import { BASE_URL } from "@/lib/constants";
import { type MetadataRoute } from "next";
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: BASE_URL,
lastModified: new Date(),
changeFrequency: "yearly",
priority: 1,
},
];
}

View File

@@ -1 +0,0 @@
export const BASE_URL = "https://investingfire.com/";