DEV Community

Cover image for Analyzr: Instant analytics for your apps 🚀
Arjun Vijay Prakash
Arjun Vijay Prakash

Posted on

Analyzr: Instant analytics for your apps 🚀

Hey there! 👋

I've been working on this new analytics tool (Analyzr) for a few weeks and would like to share it with you.

In just one line of code, your analytics are up and running—instantly.

There is also real-time stats and no limitations with notifications on Discord, plus Analyzr is completely free and open source.

This is a complete breakdown of how I built it and the features it offers.


TL;DR 🚀

Analyzr does not charge for its services and has an open-source base code with minimal hassle to implement – just a single line.

  • Free and Open-Source
  • Quick and Simple Setup
  • Comprehensive Analytics
  • Discord Notifications
  • Lightweight, Secure and Versatile

The way this is built is such that it removes the barriers of complexity as well as cost.

The end goal for Analyzr is to provide a faster and easier tool for all developers, small companies, and creators who want to monitor their website performance.


📑 Table of Contents


Why Did I Build This? 💡

Let's be honest—most analytics tools feel like a total trap.

And as a developer, I was frustrated by expensive, overcomplicated analytics tools.

I wanted something that’s simple, free, and works out of the box.

It's beyond frustrating, and honestly? It shouldn’t be like this.

That’s exactly why I created Analyzr. I wanted to make analytics simple, affordable, and accessible for everyone.

No paywalls. No hidden fees. Just the insights you actually care about.

Whether you're curious about where your visitors are coming from, what buttons they're clicking, or how fast your website is loading, Analyzr has your back.

And the best part? It's completely free.

I wanted something that’s easy for anyone to use.

Whether you’re a developer, a small business owner, or just someone with a website, Analyzr is for you.

No need to deal with complicated setups or pricey subscriptions. Just add a line of code, and you’re all set.

Analytics should be straightforward and stress-free. That's the aim.


How Did I Build this? 🛠️

I'm super excited to share the behind-the-scenes story of how I built Analyzr.

Let me break it down for you in the most honest way possible.

Tech Stack and Schema

First things first - let me tell you about the tech stack I chose.

I went with:

  • Next.js 15 & React 19
  • Supabase for the database
  • shadcn/ui for the components and charts
  • Discord.js for notifications

and this final schema structure:

Image

Tracking Script - Internal Working

Okay, this is where things got really interesting (and by interesting, I mean I spent way too many late nights debugging).

The tracking script is probably the most crucial part of Analyzr.

It's what collects all the data about website visits, user behaviour, and more.

  • Tracks page views
  • Figures out what device and browser people are using
  • Gets location data

You can see the core tracking logic here:

async function trigger(eventName, options) {
  try {
    const locationData = await getUserLocation();
    const operatingSystem = getOperatingSystem();
    const deviceType = getDeviceType();
    const { browserName } = getBrowserInfo();

    var payload = {
      event: eventName,
      url: location.href,
      domain: dataDomain,
      source,
      city: locationData.city,
      region: locationData.region,
      country: locationData.country,
      operatingSystem,
      deviceType,
      browserName,
    };

    sendRequest(payload, options);
  } catch (error) {
    console.error('Error in trigger:', error);
  }
}
Enter fullscreen mode Exit fullscreen mode

I spent a ridiculous amount of time making sure this was as lightweight as possible. And it paid off, the whole script is less than 6KB.

Tracking Performance with Google PageSpeed Insights API

Here's how the "website performance" magic happens:

const apiUrl = `https://pagespeedonline.googleapis.com/pagespeedonline/v5/runPagespeed?url=${encodedUrl}&category=performance&category=accessibility&category=best-practices&category=seo&key=${API_KEY}`;
Enter fullscreen mode Exit fullscreen mode

Before moving ahead, you should know that this API returns this massive JSON object with ALL the metrics you could ever want.

But of course, we need to make sense of it:

const metrics: PerformanceMetrics = {
  firstContentfulPaint: Math.round(data.lighthouseResult.audits['first-contentful-paint'].numericValue),
  largestContentfulPaint: Math.round(data.lighthouseResult.audits['largest-contentful-paint'].numericValue),
  timeToInteractive: Math.round(data.lighthouseResult.audits['interactive'].numericValue),
  cumulativeLayoutShift: Math.round(data.lighthouseResult.audits['cumulative-layout-shift'].numericValue * 1000),
  totalBlockingTime: Math.round(data.lighthouseResult.audits['total-blocking-time'].numericValue),
  performance: Math.round(data.lighthouseResult.categories.performance.score * 100),
  accessibility: Math.round(data.lighthouseResult.categories.accessibility.score * 100),
  bestPractices: Math.round(data.lighthouseResult.categories['best-practices'].score * 100),
  seo: Math.round(data.lighthouseResult.categories.seo.score * 100),
  speedIndex: Math.round(data.lighthouseResult.audits['speed-index'].numericValue),
};
Enter fullscreen mode Exit fullscreen mode

The next thing: I needed to find a way to combine various metrics into something meaningful.

The secret? I take the average of each metric and then categorize the results based on those averages.

function PerformanceScoreCard({ metrics }: { metrics: PerformanceMetrics }) {
  const calculateOverallScore = () => {
    const scores = [
      metrics.performance,
      metrics.accessibility,
      metrics.bestPractices,
      metrics.seo
    ].filter(score => score !== null);

    if (scores.length === 0) return null;

    return Math.round(scores.reduce((a, b) => (a ?? 0) + (b ?? 0), 0) / scores.length);
  };
}
Enter fullscreen mode Exit fullscreen mode
export const getCategory = (score: number) => {
  if (score >= 90) return { 
    label: 'EXCELLENT', 
    color: 'text-green-500', 
    bg: 'bg-green-500/20', 
    isGood: true 
  };
  if (score >= 80) return { 
    label: 'GOOD', 
    color: 'text-blue-500', 
    bg: 'bg-blue-500/20', 
    isGood: true 
  };
  if (score >= 70) return { 
    label: 'AVERAGE', 
    color: 'text-yellow-500', 
    bg: 'bg-yellow-500/20', 
    isGood: false 
  };
  if (score >= 50) return { 
    label: 'POOR', 
    color: 'text-orange-500', 
    bg: 'bg-orange-500/20', 
    isGood: false 
  };
  return { 
    label: 'CRITICAL', 
    color: 'text-red-500', 
    bg: 'bg-red-500/20', 
    isGood: false 
  };
};
Enter fullscreen mode Exit fullscreen mode

Additionally, I'll display improvement cards when one or more metrics decline.

export const getRecommendations = (metrics: PerformanceMetrics) => {
  const recommendations = [];

  if (metrics.performance < 90) {
    recommendations.push({
      title: "Speed Optimization",
      description: "Consider optimizing images and implementing caching strategies",
      metric: metrics.performance,
      icon: ""
    });
  }
  if (metrics.firstContentfulPaint > 1800) {
    recommendations.push({
      title: "First Contentful Paint",
      description: "Reduce server response time and minimize render-blocking resources",
      metric: `${(metrics.firstContentfulPaint / 1000).toFixed(1)}s`,
      icon: "🎨"
    });
  }
  if (metrics.accessibility < 90) {
    recommendations.push({
      title: "Accessibility",
      description: "Improve ARIA labels and contrast ratios",
      metric: metrics.accessibility,
      icon: ""
    });
  }
  if (metrics.cumulativeLayoutShift > 0.1) {
    recommendations.push({
      title: "Layout Stability",
      description: "Reduce layout shifts by specifying image dimensions",
      metric: metrics.cumulativeLayoutShift.toFixed(3),
      icon: "📏"
    });
  }
  if (metrics.seo < 90) {
    recommendations.push({
      title: "SEO Optimization",
      description: "Ensure all pages have meta descriptions and proper heading structure",
      metric: metrics.seo,
      icon: "🔍"
    });
  }
  if (metrics.totalBlockingTime > 300) {
    recommendations.push({
      title: "Interactivity",
      description: "Reduce JavaScript execution time and split long tasks",
      metric: `${(metrics.totalBlockingTime / 1000).toFixed(1)}s`,
      icon: ""
    });
  }

  return recommendations.slice(0, 6);
};
Enter fullscreen mode Exit fullscreen mode

Example:

Image

Database Actions

The database structure was... well, let's just say it evolved quite a bit from my initial design.

I started with a super simple schema and kept adding to it as I realized I needed more features.

Here's what happens when data comes in:

  • The tracking script sends data to our API
  • We process it and store it in Supabase
  • The data gets organized into different tables for:
    • Page views
    • Custom events
    • Performance metrics
    • User sessions

One of the cooler features I implemented was real-time analytics. Check out how the data gets processed: (this is on the UI side)

const processData = () => {
  const data: Record<string, { date: string; pageViews: number; visits: number }> = {};

  // Process visits
  visits
    .filter(visit => filterDataByTimePeriod(new Date(visit.created_at)))
    .forEach(visit => {
      const timestamp = visit.created_at;
      if (!data[timestamp]) {
        data[timestamp] = { date: timestamp, pageViews: 0, visits: 1 };
      } else {
        data[timestamp].visits++;
      }
    });

  // Process pageViews
  pageViews
    .filter(view => filterDataByTimePeriod(new Date(view.created_at)))
    .forEach(view => {
      const timestamp = view.created_at;
      if (!data[timestamp]) {
        data[timestamp] = { date: timestamp, pageViews: 1, visits: 0 };
      } else {
        data[timestamp].pageViews++;
      }
    });

  // Sort by timestamp
  const sortedData = Object.values(data).sort(
    (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()
  );

  // Group the data based on time period
  const groupedData = groupByTimeUnit(sortedData, timePeriod);

  // Sort the final data
  return Object.values(groupedData).sort((a, b) => {
    if (a.date.includes('AM') || a.date.includes('PM')) {
      // Sort hours
      const hourA = parseInt(a.date.split(' ')[0]);
      const ampmA = a.date.split(' ')[1];
      const hourB = parseInt(b.date.split(' ')[0]);
      const ampmB = b.date.split(' ')[1];

      if (ampmA === ampmB) return hourA - hourB;
      return ampmA === 'AM' ? -1 : 1;
    } else {
      return new Date(a.date).getTime() - new Date(b.date).getTime();
    }
  });
};
Enter fullscreen mode Exit fullscreen mode

Client-Side UI

The UI was both fun and challenging to build.

I wanted to make something that looked professional but also felt friendly and approachable.

Some of my favorite parts:

  • The dashboard layout (which took way too many iterations to get right)
  • The analytics charts (shadcn UI X Recharts is amazing!)
  • The custom event tracking UI

Image

Image


Who Is This For? 🎯

Image

Whether you’re a developer, small business owner, or just someone running a side project, Analyzr is built for you.

  • Developers: Track your apps or personal projects effortlessly.
  • Business owners: Monitor traffic and performance without expensive tools.
  • Anyone: Need analytics? Custom events? I've got you covered.

What Can It Do? 🚀

Image

Here’s a quick overview of what Analyzr brings to the table:

  • Real-Time Tracking: You can see live stats of what’s happening on your site.
  • Custom Events: Keep an eye on specific actions, like when users click buttons.
  • Performance Insights: Track how fast your pages load and get tips on how to make ‘em better.
  • Visitor Stats: Find out what devices, browsers, and locations your visitors use.
  • Discord Alerts: Get instant alerts in Discord about traffic spikes, errors, or other important events - as you set it.

It’s analytics, but way simpler!


How Does It Work? 🤔

Image

  1. Add the tracking script to your site.
  2. It gathers data and sends it safely to our Supabase database.
  3. Just log in to your dashboard to check out the charts and stats.

Are you curious about how it works? For the source code, take a look at our GitHub repo!


Discord Integration 🤖

Image

Discord integration is one of Analyzr's cool features. You can:

  • Notifies you when website traffic spikes
  • You can track any custom event or errors.
  • Get real-time updates on the go without having to constantly check your dashboard.

It takes just a couple of minutes to set one up so that you won't miss any beat.


Custom Event Tracking 📊

One of Analyzr's powerful features is custom event tracking.

Here's how easy it is:

Using JavaScript/Node.js:

Image

Using Python:

Image

Once you’re all set up, you can:

  • Track any custom event, which occurs on your website
  • Receive notifications to Discord to be informed when such occurrences take place
  • It is also useful to go ahead and add some custom fields and emojis to make those notifications look a little better.
  • And of course, watch all these events in your dashboard.

And guess what?

Unlike other analytics tools that charge fees for tracking custom events, Analyzr offers this feature for free!


What Makes This Different? 💪

  1. It’s Free: No trials, no fees—free forever.
  2. Super Easy Setup: One line of code, and you’re good to go.
  3. Discord Notifications: Stay in the loop without lifting a finger.
  4. Open-Source: You can customize it however you want.

Want to Try It? 📦

It's super easy to get started:

1. Add the Tracking Script

For Vanilla/React apps(index.html):

Image

For Next.js apps(layout.tsx):

Image

2. Deploy your app to production.

3. aannnddd.... It's done!

After you are done, start seeing real analytics on your dashboard.


FAQs ❓

Is it really free?
Yep! No fees, and no premium features—it’s all free.

Will it slow down my site?
Not at all. Our tracking script is lightweight and async.

Is my data secure?
Absolutely! Your data stays private and can be deleted anytime.

Can I use it with any platform?
Yes, it works with Next.js, React, Vue, plain HTML, and more.


Final Thoughts ✨

Analyzr is here to simplify analytics for everyone.

Exciting news! Analyzr is releasing tomorrow, i.e. 24th November @ 1:31 PM IST (12:01 AM PST), so watch out for the launch on Product Hunt!

Try it, star it, and let me know what you think.

For detailed setup instructions and advanced features, check out our documentation!

Check it out on Analyzr, view the source on GitHub, or see our launch on Product Hunt!

Have questions or ideas?

Drop an issue on GitHub, DM me on X @ArjunCodess or Discord @ArjunCodess.

I'd love to hear from you!

Happy tracking! 📊
Thanks for 32383!

Top comments (1)

Collapse
 
anmolbaranwal profile image
Anmol Baranwal

Great work man 🔥