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", "clsx": "^2.1.1",
"lucide-react": "^0.503.0", "lucide-react": "^0.503.0",
"next": "^15.2.3", "next": "^15.2.3",
"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",
@ -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": { "node_modules/next/node_modules/postcss": {
"version": "8.4.31", "version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",

View File

@ -25,7 +25,6 @@
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.503.0", "lucide-react": "^0.503.0",
"next": "^15.2.3", "next": "^15.2.3",
"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",

View File

@ -507,7 +507,7 @@ export default function FireCalculatorForm() {
</CardContent> </CardContent>
</Card> </Card>
{result.yearlyData && result.yearlyData.length > 0 && ( {result && result.yearlyData && result.yearlyData.length > 0 && (
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Financial Projection</CardTitle> <CardTitle>Financial Projection</CardTitle>
@ -549,7 +549,7 @@ export default function FireCalculatorForm() {
} else if (value >= 1000) { } else if (value >= 1000) {
return `${(value / 1000).toFixed(0)}K`; return `${(value / 1000).toFixed(0)}K`;
} }
return value.toString(); return `${value}`;
}} }}
width={80} width={80}
/> />
@ -560,7 +560,7 @@ export default function FireCalculatorForm() {
.payload as (typeof result.yearlyData)[0]; .payload as (typeof result.yearlyData)[0];
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} (Age: ${data.age})`}</p>
<p className="text-primary">{`Balance: ${formatNumber(data.balance)}`}</p> <p className="text-primary">{`Balance: ${formatNumber(data.balance)}`}</p>
{result.fireNumber && ( {result.fireNumber && (
<p className="text-destructive">{`FIRE Number: ${formatNumber(result.fireNumber)}`}</p> <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 "@/styles/globals.css";
import PlausibleProvider from "next-plausible";
import { type Metadata } 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";
export const metadata: Metadata = { export const metadata: Metadata = {
title: title:
"InvestingFIRE Calculator | Plan Your Financial Independence & Early Retirement", "FIRE Calculator - Plan Your Financial Independence & Early Retirement",
description: 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" }], icons: [{ rel: "icon", url: "/favicon.ico" }],
}; };
@ -22,16 +21,7 @@ export default function RootLayout({
}: Readonly<{ children: React.ReactNode }>) { }: Readonly<{ children: React.ReactNode }>) {
return ( return (
<html lang="en" className={geist.variable}> <html lang="en" className={geist.variable}>
<PlausibleProvider <body>{children}</body>
domain="investingfire.com"
customDomain="https://analytics.schulze.network"
selfHosted={true}
enabled={true}
trackOutboundLinks={true}
>
<WebVitals />
<body>{children}</body>
</PlausibleProvider>
</html> </html>
); );
} }

View File

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