How to track conversions without cookies

Cookie banners exist because of cross‑site ad tracking, not product analytics. You can track signups, purchases, and every conversion that matters without setting a single cookie.

You shipped a project. People visit. Some of them sign up, most don’t. You want to know the conversion rate, where people drop off, and what’s actually working. But every analytics tool you evaluate requires a cookie banner before you can measure anything.

Most developers accept this as the cost of analytics. They add Google Analytics, bolt on a consent manager, and move on. The cookie banner becomes part of the site’s furniture. But for side projects and indie SaaS, that banner isn’t just clutter. It erodes trust with privacy‑conscious users and adds a dependency you have to maintain.

That’s a false trade‑off. Cookies solve a specific problem: tracking users across multiple websites for ad targeting. If you’re only measuring what happens on your own site, you don’t need them. This guide covers how to set up real conversion tracking with Janus: signups, purchases, multi‑step funnels, and drop‑off analysis. No cookies, no fingerprinting, no consent banners.

Already have Janus installed? Skip ahead to the code examples. New to Janus? Add it in 5 minutes and come back.

That cross‑site tracking is the specific behavior that privacy regulations target. According to the ePrivacy Directive, cookies require user consent unless they are "strictly necessary" for a service the user requested. Analytics cookies don’t qualify. They are useful to you as a site owner, but not necessary for the visitor. That’s why Google Analytics requires a consent banner.

Product analytics has different requirements than ad tracking. You need to know whether someone visited your pricing page, clicked signup, and finished onboarding. All of that happens on your domain. None of it requires cross‑site cookies, browser fingerprinting, or third‑party scripts.

The consent requirement comes from the tracking mechanism, not from analytics itself. localStorage is first‑party storage scoped to your origin. Other websites cannot read it. It is not covered by the ePrivacy Directive’s cookie consent rules because it does not enable cross‑site tracking. GDPR still applies to processing of personal data, but a random anonymous ID in localStorage does not identify a natural person under GDPR’s definition.

Remove the cookies and fingerprinting, and the consent requirement goes with them. Read more about how Janus handles privacy and why it was built this way.

Sessions use a separate sessionId stored in sessionStorage. This ID is scoped to a single browser tab and expires when the tab closes. It tracks multi‑step flows within a visit without persisting anything beyond the session.

Neither localStorage nor sessionStorage is accessible to other websites. They are scoped to your origin, which means your analytics data cannot leak to third parties.

The difference between the two IDs matters for conversion tracking. Use distinctId to measure conversions that span days or weeks: a user visits your landing page Monday, reads docs Tuesday, signs up Friday. Use sessionId for single‑session flows: landing page to pricing to checkout in one sitting.

IP addresses are used transiently for country‑level geolocation via GeoIP lookup and then discarded. They are never written to the database. According to GDPR guidance, transient processing without storage does not constitute personal data collection.

What Janus captures automatically

Every event includes context the SDK collects without any configuration:

  • Page URL and path.
  • Referrer: where the visitor came from.
  • Screen and viewport dimensions.
  • Browser and operating system via user agent.
  • Language and timezone.
  • Country, derived from IP at ingest time. The IP itself is discarded.
  • UTM parameters, if present in the URL.

The Janus loader is under 1 KB and loads asynchronously after the page paints. It is designed to have zero measurable impact on Core Web Vitals. No cookies, no fingerprinting, no third‑party data sharing.

Implementation

How to set up conversion events

Add the Janus script and initialize it with your API key. If you already have Janus running, skip to the tracking examples below.

index.html
<script src="https://addjanus.ca/janus.js"></script>
<script>
Janus.init('jns_your_api_key_here');
</script>

Page views are tracked automatically, including SPA navigation. For conversions, you call Janus.track() at the moments that matter to your business.

Track conversion events

Call Janus.track() when a conversion happens. First argument: event name. Second argument: an optional properties object.

conversions.js
// User completes signup
Janus.track('signup_completed', {
plan: 'free',
source: 'pricing-page'
});
// User makes a purchase
Janus.track('purchase_completed', {
amount: 29,
currency: 'USD',
plan: 'pro'
});
// User hits a key activation moment
Janus.track('first_project_created', {
template: 'blank'
});

Use verb_noun naming: signup_started, checkout_completed, payment_failed, trial_expired. This convention keeps your event log scannable, makes funnel steps easy to define, and simplifies querying via the HTTP API or through your AI coding agent.

Properties in the second argument go into the event’s payload. Put business‑specific data here: plan name, price, variant, referral source. Janus captures context separately and automatically, so don’t duplicate fields like referrer or page URL in your payload. Combined payload and context must stay under 10 KB per event. See the API reference for the full Janus.track() signature and the best practices guide for naming conventions.

Track conversions in React

In a React app, call Janus.track() from event handlers in any component. The SDK is available on window after initialization.

PricingCard.jsx
function PricingCard({ plan, price }) {
const handleSubscribe = () => {
window.Janus?.track('checkout_started', {
plan,
price,
source: 'pricing-page'
});
// proceed to checkout...
};
return (
<button onClick={handleSubscribe}>
Subscribe to {plan}
</button>
);
}

Track a multi‑step conversion flow

For conversions that span multiple actions, fire an event at each step. Janus links them by distinctId automatically.

signup-flow.js
// Step 1: User lands on signup page
Janus.track('signup_started');
// Step 2: User submits the form
Janus.track('signup_submitted', {
method: 'email'
});
// Step 3: User verifies their email
Janus.track('email_verified');
// Step 4: User completes onboarding
Janus.track('onboarding_completed', {
steps_skipped: 0
});
// Step 5: User creates their first project
Janus.track('first_project_created');

Each event fires independently at the moment it happens. You don’t need to pass step numbers, reference previous events, or chain anything together. Janus connects them into a funnel using Journeys.

What journey results show

For each step in the funnel, Janus calculates:

  • Entered: how many users or sessions reached this step.
  • Conversion rate: percentage of step 1 entrants who made it here.
  • Step‑over‑step rate: percentage of the previous step that continued.
  • Drop‑off: absolute count lost between this step and the last.
  • Median time: median seconds between consecutive steps.

You choose the match scope when creating a journey. Match by distinctId for conversions that span multiple visits over days or weeks. This catches users who sign up on a different day than they first visited, which is common for SaaS products with an evaluation period. Match by sessionId for single‑session flows where you want to measure immediate friction.

Conversion windows range from 5 minutes to 30 days. The default is 24 hours. If a user completes step 1 but doesn’t reach step 2 within the window, they count as drop‑off. Wider windows capture more conversions but dilute the signal. For most SaaS signup funnels, 7 days covers the typical evaluation period.

A practical example: you define a 3‑step journey with signup_startedemail_verifiedfirst_project_created, matched by distinctId over a 7‑day window. Results show 200 users started signup, 140 verified their email (70% step rate), and 80 created a project (57% step rate, 40% overall conversion). The median time between steps tells you whether the drop‑off is friction or intent. Thirty seconds between steps means people are stuck on the form. Three days means they’re evaluating.

The value of funnel data is in the comparisons. Run the same journey across different time periods to see if conversion improves after a change. Run it filtered by referrer to see whether organic traffic converts differently than direct. Journeys also accept page view steps, so you can mix URL paths with custom events: /pricingcheckout_startedpurchase_completed.

Tips for effective journeys

  • Start with 3‑5 steps. More steps reveal more drop‑off points but need more traffic to produce meaningful numbers.
  • Name steps after actions, not pages. signup_completed is more reliable than matching on /signup because URL structures change.
  • Test with sessionId first for quick feedback, then switch to distinctId once you have enough multi‑day data.

Journeys are available on Pro plans. The free tier tracks all events but does not include funnel analysis. See the Journeys documentation for setup instructions and step types.

Trade‑offs

What cookieless tracking leaves out

Removing cookies removes specific capabilities. As of May 2026, these are the gaps:

  • No cross‑device tracking. A user who browses on their phone and converts on their laptop counts as two visitors.
  • No persistent identity after storage clears. If a user clears localStorage, their next visit starts a fresh distinctId. Incognito windows always start fresh.
  • No demographic data beyond country. No age, gender, or interest segments.
  • No ad platform integration. Janus doesn’t feed conversion data to Google Ads, Meta Ads, or any advertising network.

For side projects and early‑stage SaaS, these gaps rarely affect the decisions you make. You need conversion rates and drop‑off points. You need to know which step loses the most users and how long people take between steps. You don’t need an identity graph to answer those questions.

Accuracy is high for the metrics that matter. Conversion rates are calculated from real events on your domain, not from probabilistic cross‑device matching. The numbers represent real actions from real visitors. The main limitation is that some returning visitors are counted as new if they cleared storage, which slightly inflates top‑of‑funnel numbers without affecting step‑over‑step rates.

If you run paid ad campaigns that require cross‑platform attribution, you can run GA alongside Janus on the pages where ads convert.

The Point

Conversion data without consent banners

Janus gives you conversion tracking that works without cookies, fingerprinting, or consent popups. As of May 2026:

  • Custom conversion events via Janus.track() with named events and typed properties.
  • Multi‑step funnels via Journeys with conversion rates, drop‑off counts, and median time between steps.
  • Cookieless identity via localStorage and sessionStorage, scoped to your domain. No cross‑site tracking.
  • A free tier: 10,000 events per month, 3 API keys, 3 months data retention. No credit card required.

New to Janus? Add analytics to your side project in 5 minutes. Already tracking events? Connect Janus to your AI coding agent and check conversion rates from your editor.

Frequently Asked Questions

Do I need a cookie banner if I use Janus?

No. Janus does not set cookies, use fingerprinting, or engage in cross-site tracking. Cookie consent banners are required by the ePrivacy Directive for services that set cookies. Janus uses localStorage, which is first-party storage scoped to your domain. No consent banner is needed for Janus traffic.

How does Janus identify returning visitors without cookies?

Janus generates a random anonymous ID called a distinctId on first visit and stores it in the browser localStorage. This ID persists across tabs and browser restarts. It contains no personal information. If a user clears localStorage, they appear as a new visitor on their next visit.

Can I track purchases and signups without cookies?

Yes. Call Janus.track() with an event name and properties object at the moment a conversion happens. For example: Janus.track('purchase_completed', { plan: 'pro', amount: 29 }). All conversion events use localStorage-based identity, not cookies.

What is the difference between distinctId and sessionId?

distinctId is stored in localStorage and persists across browser sessions. Use it for conversions that span multiple visits over days or weeks. sessionId is stored in sessionStorage and expires when the browser tab closes. Use it for single-session conversion flows.

How accurate is cookieless conversion tracking?

Conversion rates are calculated from real events on your domain, not from probabilistic matching. The main limitation is that users who clear localStorage appear as new visitors, which can slightly inflate top-of-funnel counts. Step-over-step conversion rates are not affected because both steps use the same ID within a session.

Is cookieless tracking GDPR-compliant?

Janus is designed to be GDPR-friendly. It does not use cookies, fingerprinting, or cross-site tracking. IP addresses are used transiently for country-level geolocation and never stored. The random localStorage ID does not identify a natural person under GDPR definition. No personal data is collected or shared with third parties.

How small is the Janus tracking script?

The Janus loader script is under 1 KB. It loads asynchronously after the page paints, so it does not block rendering or affect Core Web Vitals. The full SDK loads from the Janus CDN at runtime with no npm install and no impact on your bundle size.

Track conversions. Skip the cookie banner.

Free tier. No credit card. No consent popup.

Get started with Janus