Compare commits

..

No commits in common. "acec8494281b06438b3ddc5e36fcc6c8053e33c6" and "64669e5f588d8b54013b54e31c5c2ff0b8e651e5" have entirely different histories.

7 changed files with 76 additions and 237 deletions

15
package-lock.json generated
View File

@ -17,7 +17,6 @@
"clsx": "^2.1.1",
"lucide-react": "^0.503.0",
"next": "^15.2.3",
"next-plausible": "^3.12.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.56.1",
@ -5090,20 +5089,6 @@
}
}
},
"node_modules/next-plausible": {
"version": "3.12.4",
"resolved": "https://registry.npmjs.org/next-plausible/-/next-plausible-3.12.4.tgz",
"integrity": "sha512-cD3+ixJxf8yBYvsideTxqli3fvrB7R4BXcvsNJz8Sm2X1QN039WfiXjCyNWkub4h5++rRs6fHhchUMnOuJokcg==",
"license": "MIT",
"funding": {
"url": "https://github.com/4lejandrito/next-plausible?sponsor=1"
},
"peerDependencies": {
"next": "^11.1.0 || ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 ",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/next/node_modules/postcss": {
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",

View File

@ -25,7 +25,6 @@
"clsx": "^2.1.1",
"lucide-react": "^0.503.0",
"next": "^15.2.3",
"next-plausible": "^3.12.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-hook-form": "^7.56.1",

View File

@ -507,7 +507,7 @@ export default function FireCalculatorForm() {
</CardContent>
</Card>
{result.yearlyData && result.yearlyData.length > 0 && (
{result && result.yearlyData && result.yearlyData.length > 0 && (
<Card>
<CardHeader>
<CardTitle>Financial Projection</CardTitle>
@ -549,7 +549,7 @@ export default function FireCalculatorForm() {
} else if (value >= 1000) {
return `${(value / 1000).toFixed(0)}K`;
}
return value.toString();
return `${value}`;
}}
width={80}
/>
@ -560,7 +560,7 @@ export default function FireCalculatorForm() {
.payload as (typeof result.yearlyData)[0];
return (
<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} (Age: ${data.age})`}</p>
<p className="text-primary">{`Balance: ${formatNumber(data.balance)}`}</p>
{result.fireNumber && (
<p className="text-destructive">{`FIRE Number: ${formatNumber(result.fireNumber)}`}</p>

View File

@ -1,77 +0,0 @@
"use client";
import { usePlausible } from "next-plausible";
import { useReportWebVitals } from "next/web-vitals";
interface Metric {
/**
* The name of the metric (in acronym form).
*/
name: "CLS" | "FCP" | "FID" | "INP" | "LCP" | "TTFB";
/**
* The current value of the metric.
*/
value: number;
/**
* The rating as to whether the metric value is within the "good",
* "needs improvement", or "poor" thresholds of the metric.
*/
rating: "good" | "needs-improvement" | "poor";
/**
* The delta between the current value and the last-reported value.
* On the first report, `delta` and `value` will always be the same.
*/
delta: number;
/**
* A unique ID representing this particular metric instance. This ID can
* be used by an analytics tool to dedupe multiple values sent for the same
* metric instance, or to group multiple deltas together and calculate a
* total. It can also be used to differentiate multiple different metric
* instances sent from the same page, which can happen if the page is
* restored from the back/forward cache (in that case new metrics object
* get created).
*/
id: string;
/**
* Any performance entries relevant to the metric value calculation.
* The array may also be empty if the metric value was not based on any
* entries (e.g. a CLS value of 0 given no layout shifts).
*/
entries: PerformanceEntry[];
/**
* The type of navigation.
*
* This will be the value returned by the Navigation Timing API (or
* `undefined` if the browser doesn't support that API), with the following
* exceptions:
* - 'back-forward-cache': for pages that are restored from the bfcache.
* - 'back_forward' is renamed to 'back-forward' for consistency.
* - 'prerender': for pages that were prerendered.
* - 'restore': for pages that were discarded by the browser and then
* restored by the user.
*/
navigationType:
| "navigate"
| "reload"
| "back-forward"
| "back-forward-cache"
| "prerender"
| "restore";
}
export function WebVitals() {
const plausible = usePlausible();
useReportWebVitals((metric: Metric) => {
plausible("web-vitals", {
props: {
[metric.name]: metric.rating,
},
});
});
return <></>;
}

View File

@ -1,14 +1,13 @@
import "@/styles/globals.css";
import PlausibleProvider from "next-plausible";
import { type Metadata } from "next";
import { Geist } from "next/font/google";
import { WebVitals } from "./components/web-vitals";
export const metadata: Metadata = {
title:
"InvestingFIRE Calculator | Plan Your Financial Independence & Early Retirement",
"FIRE Calculator - Plan Your Financial Independence & Early Retirement",
description:
"Achieve Financial Independence, Retire Early (FIRE) with the InvestingFIRE calculator. Get personalized projections and investing advice to plan your journey.",
"Calculate your FIRE number, estimate your retirement age, and plan your path to financial independence with this comprehensive FIRE calculator.",
icons: [{ rel: "icon", url: "/favicon.ico" }],
};
@ -22,16 +21,7 @@ export default function RootLayout({
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="en" className={geist.variable}>
<PlausibleProvider
domain="investingfire.com"
customDomain="https://analytics.schulze.network"
selfHosted={true}
enabled={true}
trackOutboundLinks={true}
>
<WebVitals />
<body>{children}</body>
</PlausibleProvider>
</html>
);
}

View File

@ -9,54 +9,41 @@ import {
export default function HomePage() {
return (
<main className="text-primary-foreground to-destructive from-secondary flex min-h-screen flex-col items-center bg-gradient-to-b p-4">
<div className="container mx-auto flex flex-col items-center justify-center gap-4 px-4 py-16 text-center">
<h1 className="from-primary via-primary-foreground to-primary bg-gradient-to-r bg-clip-text text-5xl font-extrabold tracking-tight text-transparent drop-shadow-md sm:text-[5rem]">
InvestingFIRE
<div className="container mx-auto flex flex-col items-center justify-center gap-12 px-4 py-16">
<h1 className="text-primary-foreground text-5xl font-extrabold tracking-tight sm:text-[5rem]">
FIRE Calculator
</h1>
<p className="text-primary-foreground/90 text-xl font-semibold md:text-2xl">
The #1 FIRE Calculator
</p>
<div className="mt-8 w-full max-w-2xl">
<FireCalculatorForm />
</div>
</div>
{/* Added SEO Content Sections */}
<div className="container mx-auto max-w-4xl px-4 py-8 text-left">
<section className="mb-12">
<h2 className="mb-4 text-3xl font-bold">
What is FIRE? Understanding Financial Independence and Early
Retirement
</h2>
<h2 className="mb-4 text-3xl font-bold">What is FIRE?</h2>
<p className="mb-4 text-lg leading-relaxed">
FIRE stands for &quot;Financial Independence, Retire Early.&quot;
It&apos;s a movement focused on aggressive saving and strategic
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.
It&apos;s a movement focused on aggressive saving and investing to
build a large enough portfolio that the returns can cover living
expenses indefinitely. Achieving FIRE means you are no longer
dependent on traditional employment to fund your lifestyle, giving
you the freedom to pursue passions, travel, or simply enjoy life
without the need for a regular paycheck.
</p>
<p className="text-lg leading-relaxed">
The core principle involves maximizing your savings rate (often
50%+) and investing wisely, typically in low-cost, diversified
assets like index funds. Your &quot;FIRE number&quot; the capital
needed is often estimated as 25 times your desired annual
spending, derived from the 4% safe withdrawal rate guideline. This
FIRE calculator helps you personalize this estimate.
The core principle often involves saving a high percentage of income
(sometimes 50% or more) and investing it wisely, typically in
low-cost index funds. The target amount, often called the &quot;FIRE
number,&quot; is usually calculated as 25 times your desired annual
spending, based on the 4% safe withdrawal rate rule.
</p>
</section>
<section className="mb-12">
<h2 className="mb-4 text-3xl font-bold">
How This FIRE Calculator Provides Investing Insights
</h2>
<h2 className="mb-4 text-3xl font-bold">How This Calculator Works</h2>
<p className="mb-4 text-lg leading-relaxed">
This calculator helps visualize your path to FIRE by projecting
investment growth based on your inputs. Understanding these
projections is a key piece of investing advice for long-term
planning. Here&apos;s a breakdown of the inputs:
This calculator helps you estimate your path to FIRE based on your
current financial situation and future projections. Here&apos;s a
breakdown of the inputs:
</p>
<ul className="mb-4 ml-6 list-disc space-y-2 text-lg">
<li>
@ -91,19 +78,19 @@ export default function HomePage() {
</ul>
<p className="text-lg leading-relaxed">
The calculator simulates your investment growth year by year,
incorporating monthly contributions, the power of compound growth (a
core investing principle), and inflation&apos;s impact on your
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.
factoring in monthly contributions, compound growth, and
inflation&apos;s effect on your target allowance. It then determines
the age at which your accumulated capital is sufficient to sustain
your desired, inflation-adjusted monthly allowance throughout your
expected retirement years until your specified life expectancy. It
estimates your &quot;FIRE Number&quot; (the capital needed at
retirement) and the age you might reach it.
</p>
</section>
<section className="mb-12">
<h2 className="mb-4 text-3xl font-bold">
FIRE & Investing Frequently Asked Questions (FAQ)
Frequently Asked Questions (FAQ)
</h2>
<Accordion type="single" collapsible className="w-full">
<AccordionItem value="item-1">
@ -123,16 +110,15 @@ export default function HomePage() {
<AccordionItem value="item-2">
<AccordionTrigger className="text-xl font-semibold">
Is the Expected Growth Rate realistic? Finding the right
investing advice often starts here.
Is the Expected Growth Rate realistic?
</AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed">
Historically, diversified stock market investments have returned
around 7-10% annually long-term (before inflation). A rate of 7%
(after fees) is common, but remember past performance
doesn&apos;t guarantee future results, a fundamental piece of
investing advice. Choose a rate reflecting your risk tolerance
and investment strategy.
around 7-10% annually over the long term, before inflation. A
rate of 7% (after fees) is often used as a reasonable estimate,
but past performance doesn&apos;t guarantee future results.
It&apos;s crucial to choose a rate you feel comfortable with and
understand the associated risks.
</AccordionContent>
</AccordionItem>
@ -152,15 +138,15 @@ export default function HomePage() {
<AccordionItem value="item-4">
<AccordionTrigger className="text-xl font-semibold">
Can I really retire early with FIRE?
Can I really retire early?
</AccordionTrigger>
<AccordionContent className="text-lg leading-relaxed">
Retiring significantly early is achievable but demands
discipline, a high savings rate, and smart investing. Success
depends on income, expenses, savings habits, and investment
returns. Use this FIRE calculator as a planning tool,
understanding it provides estimates based on your assumptions
and chosen investing approach.
Retiring significantly earlier than traditional retirement age
is possible but requires discipline, a high savings rate, and
consistent investment growth. The feasibility depends heavily on
your income, expenses, savings habits, and investment returns.
Use this calculator as a tool for planning and motivation, but
remember it provides estimates based on your inputs.
</AccordionContent>
</AccordionItem>
@ -196,24 +182,26 @@ export default function HomePage() {
{/* Optional: Add a section for relevant resources/links here */}
<section className="mb-12">
<h2 className="mb-4 text-3xl font-bold">
FIRE Journey & Investing Resources
Further Reading & Resources
</h2>
<p className="mb-6 text-lg leading-relaxed">
Ready to dive deeper into FIRE and solidify your investing strategy?
Explore these valuable resources for financial independence planning
and investing advice:
Want to learn more about FIRE and continue your journey to financial
independence? Here are some valuable resources to explore:
</p>
<div className="bg-secondary/20 my-8 rounded-md p-4 text-lg">
<p className="font-semibold">Getting Started with FIRE:</p>
<ol className="ml-6 list-decimal space-y-1">
<li>
Calculate your personal numbers using this FIRE calculator and
other tools.
Read foundational content like Mr. Money Mustache&apos;s simple
math article
</li>
<li>
Seek sound investing advice and consider joining communities
like r/Fire for support.
Calculate your personal numbers using this and other FIRE
calculators
</li>
<li>
Join communities like r/Fire to ask questions and find support
</li>
<li>Explore books and podcasts to deepen your understanding</li>
</ol>
@ -221,24 +209,24 @@ export default function HomePage() {
<div className="grid gap-8 md:grid-cols-2">
<div>
<h3 className="mb-3 text-xl font-semibold">
Blogs & Investing Websites
</h3>
<h3 className="mb-3 text-xl font-semibold">Blogs & Websites</h3>
<ul className="ml-6 list-disc space-y-2 text-lg">
<li>
<a
href="https://www.mrmoneymustache.com/2012/01/13/the-shockingly-simple-math-behind-early-retirement/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Mr. Money Mustache - Simple Math Behind Early Retirement &
Investing
Mr. Money Mustache - The Shockingly Simple Math Behind Early
Retirement
</a>
</li>
<li>
<a
href="https://www.playingwithfire.co/resources"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Playing With FIRE - Comprehensive Resources
@ -248,6 +236,7 @@ export default function HomePage() {
<a
href="https://www.reddit.com/r/Fire/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
r/Fire Reddit Community
@ -257,24 +246,23 @@ export default function HomePage() {
</div>
<div>
<h3 className="mb-3 text-xl font-semibold">
Books & Investment Learning
</h3>
<h3 className="mb-3 text-xl font-semibold">Books & Learning</h3>
<ul className="ml-6 list-disc space-y-2 text-lg">
<li>
<a
href="https://www.amazon.com/Your-Money-Life-Transforming-Relationship/dp/0143115766"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Your Money or Your Life - Foundational FIRE & Investing
Principles
Your Money or Your Life - Vicki Robin & Joe Dominguez
</a>
</li>
<li>
<a
href="https://www.playingwithfire.co/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Playing With FIRE Documentary
@ -284,10 +272,10 @@ export default function HomePage() {
<a
href="https://podcasts.apple.com/us/podcast/can-you-retire-now-this-fire-calculator-will-tell-you/id1330225136?i=1000683436292"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
BiggerPockets Money Podcast - FIRE Calculators & Investing
Strategies
BiggerPockets Money Podcast - FIRE Calculators
</a>
</li>
</ul>
@ -295,13 +283,14 @@ export default function HomePage() {
<div>
<h3 className="mb-3 text-xl font-semibold">
Additional FIRE & Investing Calculators
Additional Calculators & Tools
</h3>
<ul className="ml-6 list-disc space-y-2 text-lg">
<li>
<a
href="https://walletburst.com/tools/coast-fire-calculator/"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Coast FIRE Calculator - For those considering a partial
@ -312,34 +301,26 @@ export default function HomePage() {
<a
href="https://www.empower.com/retirement-calculator"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Empower Retirement Planner - Free portfolio analysis and net
worth tracking
</a>
</li>
<li>
<a
href="https://www.investor.gov/financial-tools-calculators/calculators/compound-interest-calculator"
target="_blank"
className="text-primary hover:underline"
>
CAGR Compound Interest Calculator - Understand Investment
Growth
</a>
</li>
</ul>
</div>
<div>
<h3 className="mb-3 text-xl font-semibold">
Recent Investing & FIRE Articles
Recent Articles & Trends
</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"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
Coast FIRE: Retiring in your 30s while becoming a
@ -350,6 +331,7 @@ export default function HomePage() {
<a
href="https://www.businessinsider.com/financial-independence-retire-early-saving-loneliness-retreat-bali-making-friends-2025-2"
target="_blank"
rel="noopener noreferrer"
className="text-primary hover:underline"
>
The Social Side of FIRE: Finding Community in Financial

View File

@ -1,40 +0,0 @@
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const env = createEnv({
/**
* Specify your server-side environment variables schema here. This way you can ensure the app
* isn't built with invalid env vars.
*/
server: {
NODE_ENV: z.enum(["development", "test", "production"]),
},
/**
* Specify your client-side environment variables schema here. This way you can ensure the app
* isn't built with invalid env vars. To expose them to the client, prefix them with
* `NEXT_PUBLIC_`.
*/
client: {
// NEXT_PUBLIC_CLIENTVAR: z.string(),
},
/**
* You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
* middlewares) or client-side so we need to destruct manually.
*/
runtimeEnv: {
NODE_ENV: process.env.NODE_ENV,
// NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR,
},
/**
* Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
* useful for Docker builds.
*/
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
/**
* Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and
* `SOME_VAR=''` will throw an error.
*/
emptyStringAsUndefined: true,
});