Skip to content

Commit

Permalink
Adding example with-fingerprintjs-pro for identifying visitors (#37549)
Browse files Browse the repository at this point in the history
## Documentation / Examples

- [x] Make sure the linting passes by running `pnpm lint`
- [x] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples)

Deployed on https://with-fingerprintjs-pro.vercel.app/
  • Loading branch information
ilfa committed Jun 22, 2022
1 parent 4dfe563 commit a4117c9
Show file tree
Hide file tree
Showing 22 changed files with 777 additions and 0 deletions.
1 change: 1 addition & 0 deletions examples/with-fingerprintjs-pro/.env.local.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NEXT_PUBLIC_FPJS_PUBLIC_API_KEY=<your_fingerprintjs_pro_public_key>
38 changes: 38 additions & 0 deletions examples/with-fingerprintjs-pro/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# next.js
/.next/
/out/

# production
/build

# misc
.DS_Store
*.pem

# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*

# local env files
.env.local
.env.development.local
.env.test.local
.env.production.local

# vercel
.vercel

# typescript
*.tsbuildinfo
32 changes: 32 additions & 0 deletions examples/with-fingerprintjs-pro/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# An example app with FingerprintJS Pro

This example shows how to identify visitors using [FingerprintJS Pro](https://fingerprintjs.com/) with Next.js.

## Deploy your own

Deploy the example using [Vercel](https://vercel.com?utm_source=github&utm_medium=readme&utm_campaign=next-example):

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/git/external?repository-url=https://github.com/vercel/next.js/tree/canary/examples/with-fingerprintjs-pro&project-name=with-fingerprintjs-pro&repository-name=with-fingerprintjs-pro)

## How to use

Execute [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app) with [npm](https://docs.npmjs.com/cli/init), [Yarn](https://yarnpkg.com/lang/en/docs/cli/create/), or [pnpm](https://pnpm.io) to bootstrap the example:

```bash
npx create-next-app --example with-fingerprintjs-pro with-fingerprintjs-pro-app
# or
yarn create next-app --example with-fingerprintjs-pro with-fingerprintjs-pro-app
# or
pnpm create next-app --example with-fingerprintjs-pro with-fingerprintjs-pro-app
```

Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&utm_medium=readme&utm_campaign=next-example) ([Documentation](https://nextjs.org/docs/deployment)).

## Notes

### API key

To identify visitors, you'll need a FingerprintJS Pro account (you can [sign up for free](https://dashboard.fingerprintjs.com/signup/)).
You can learn more about API keys in the [official FingerprintJS Pro documentation](https://dev.fingerprintjs.com/docs/quick-start-guide).

Once you get public key, you need to set up the API key in the environment variable `NEXT_PUBLIC_FPJS_PUBLIC_API_KEY`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { useRouter } from 'next/router'

export const CacheStrategySelector = () => {
const router = useRouter()
const { asPath, route } = router
const value = asPath.split('/')[2]

return (
<div className="cache-option-toggler">
<h4>Cache option</h4>
<select
name="cache-option"
onChange={(event) => {
const newValue = event.currentTarget.value
const newRoute = route.replace('[cacheStrategy]', newValue)
router.push(newRoute, undefined, { scroll: false })
}}
value={value}
>
<option value="no-cache">Without cache</option>
<option value="memory-cache">Memory Cache</option>
<option value="session-storage-cache">Session Storage Cache</option>
<option value="ls-cache">Local Storage Cache</option>
</select>
</div>
)
}
37 changes: 37 additions & 0 deletions examples/with-fingerprintjs-pro/components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { PropsWithChildren } from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'
import { RouterProps } from './types'

export const Nav: React.FC = () => {
return (
<nav className="nav">
<CustomLink to="home">Home</CustomLink>
<CustomLink to="signin">Sign in</CustomLink>
</nav>
)
}

const CustomLink: React.FC<PropsWithChildren<{ to: string }>> = ({
to,
children,
}) => {
const router = useRouter()
const { pathname } = router
const { cacheStrategy } = router.query as RouterProps
const linkPathname = `/${to}/[cacheStrategy]`
const className = `nav-link${pathname === linkPathname ? ' active' : ''}`
return (
<div>
<Link
href={{
pathname: linkPathname,
query: { cacheStrategy },
}}
scroll={false}
>
<a className={className}>{children}</a>
</Link>
</div>
)
}
25 changes: 25 additions & 0 deletions examples/with-fingerprintjs-pro/components/Toggler.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { PropsWithChildren, useState } from 'react'

function Toggler({ children }: PropsWithChildren<{}>) {
const [showChildren, setShowChildren] = useState(true)

return (
<div className="toggler">
<p>
You can open the Network tab in your browser Dev Tools and see new
requests being made depending on the <b>caching algorithm</b> as you
unmount the component with the fingerprint and mount it again thus
calling the <code>useVisitorData</code> hook.
</p>
<button
className="toggle-button"
onClick={() => setShowChildren((show) => !show)}
>
{showChildren ? 'Hide' : 'Show'} visitor data
</button>
{showChildren && children}
</div>
)
}

export default Toggler
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { VisitorData } from '@fingerprintjs/fingerprintjs-pro-react'

function VisitorDataPresenter({
data,
isLoading,
error,
}: {
data?: VisitorData
isLoading?: boolean
error?: Error
}) {
if (error) {
return <p>An error occurred: {error.message}</p>
}

return (
<div className="visitor-data">
<p>
<b>Visitor ID:</b>{' '}
{isLoading
? 'Loading...'
: data
? data.visitorId
: 'not established yet'}
</p>
{data && (
<>
<p>
<b>Full visitor data:</b>
</p>
<pre className="details">{JSON.stringify(data, null, 2)}</pre>
</>
)}
</div>
)
}

export default VisitorDataPresenter
9 changes: 9 additions & 0 deletions examples/with-fingerprintjs-pro/components/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export type CacheStrategyPath =
| 'no-cache'
| 'memory-cache'
| 'session-storage-cache'
| 'ls-cache'

export type RouterProps = {
cacheStrategy: CacheStrategyPath
}
5 changes: 5 additions & 0 deletions examples/with-fingerprintjs-pro/next-env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
15 changes: 15 additions & 0 deletions examples/with-fingerprintjs-pro/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
async redirects() {
return [
{
source: '/',
destination: '/home/memory-cache',
permanent: true,
},
]
},
}

module.exports = nextConfig
19 changes: 19 additions & 0 deletions examples/with-fingerprintjs-pro/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start"
},
"dependencies": {
"@fingerprintjs/fingerprintjs-pro-react": "1.0.1",
"next": "latest",
"react": "18.1.0",
"react-dom": "18.1.0"
},
"devDependencies": {
"@types/node": "17.0.40",
"@types/react": "18.0.12",
"typescript": "4.7.3"
}
}
52 changes: 52 additions & 0 deletions examples/with-fingerprintjs-pro/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useRouter } from 'next/router'
import Head from 'next/head'
import '../styles/globals.css'
import '../styles/App.css'
import { InMemoryCache } from '../providers/InMemoryCache'
import { LocalStorageCache } from '../providers/LocalStorageCache'
import { WithoutCache } from '../providers/WithoutCache'
import { SessionStorageCache } from '../providers/SessionStorageCache'
import { CacheStrategySelector } from '../components/CacheStrategySelector'
import { Nav } from '../components/Nav'
import type { AppProps } from 'next/app'
import type { CacheStrategyPath, RouterProps } from '../components/types'

const getFingerprintJsProProviderByCacheStrategy = (
cacheStrategy: CacheStrategyPath
) => {
switch (cacheStrategy) {
case 'no-cache':
return WithoutCache
case 'ls-cache':
return LocalStorageCache
case 'session-storage-cache':
return SessionStorageCache
default:
return InMemoryCache
}
}

function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter()
const { cacheStrategy } = router.query as RouterProps

const ConfiguredFingerprintJsProProvider =
getFingerprintJsProProviderByCacheStrategy(cacheStrategy)

return (
<div className="layout">
<Head>
<title>FingerprintJs Pro example for Next.js</title>
</Head>
<CacheStrategySelector />
<main>
<ConfiguredFingerprintJsProProvider>
<Nav />
<Component {...pageProps} />
</ConfiguredFingerprintJsProProvider>
</main>
</div>
)
}

export default MyApp
38 changes: 38 additions & 0 deletions examples/with-fingerprintjs-pro/pages/home/[cacheStrategy].tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import Toggler from '../../components/Toggler'
import VisitorDataPresenter from '../../components/VisitorDataPresenter'
import { useContext } from 'react'
import {
FpjsContext,
useVisitorData,
} from '@fingerprintjs/fingerprintjs-pro-react'

function HomePage() {
const { clearCache } = useContext(FpjsContext)

return (
<section className="body">
<h3>Home page</h3>
<div>
On this page we use the <code>useVisitorData</code> hook with default
settings and identification is performed as soon as the page loads. This
is the most common use-case.
</div>
<Toggler>
<VisitorDataComponent />
</Toggler>
<button className="clear-cache-button" onClick={() => clearCache()}>
Clear cache
</button>
</section>
)
}

function VisitorDataComponent() {
const { data, isLoading, error } = useVisitorData({ extendedResult: true })

return (
<VisitorDataPresenter data={data} isLoading={isLoading} error={error} />
)
}

export default HomePage
Loading

0 comments on commit a4117c9

Please sign in to comment.