react-fathom 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +199 -0
  3. package/dist/cjs/index.cjs +410 -0
  4. package/dist/cjs/index.cjs.map +1 -0
  5. package/dist/cjs/next/index.cjs +910 -0
  6. package/dist/cjs/next/index.cjs.map +1 -0
  7. package/dist/es/index.js +381 -0
  8. package/dist/es/index.js.map +1 -0
  9. package/dist/es/next/index.js +885 -0
  10. package/dist/es/next/index.js.map +1 -0
  11. package/dist/react-fathom.js +413 -0
  12. package/dist/react-fathom.js.map +1 -0
  13. package/dist/react-fathom.min.js +3 -0
  14. package/dist/react-fathom.min.js.map +1 -0
  15. package/package.json +127 -0
  16. package/src/FathomContext.tsx +5 -0
  17. package/src/FathomProvider.test.tsx +532 -0
  18. package/src/FathomProvider.tsx +122 -0
  19. package/src/components/TrackClick.test.tsx +191 -0
  20. package/src/components/TrackClick.tsx +62 -0
  21. package/src/components/TrackPageview.test.tsx +111 -0
  22. package/src/components/TrackPageview.tsx +36 -0
  23. package/src/components/TrackVisible.test.tsx +311 -0
  24. package/src/components/TrackVisible.tsx +105 -0
  25. package/src/components/index.ts +3 -0
  26. package/src/hooks/index.ts +4 -0
  27. package/src/hooks/useFathom.test.tsx +51 -0
  28. package/src/hooks/useFathom.ts +11 -0
  29. package/src/hooks/useTrackOnClick.test.tsx +197 -0
  30. package/src/hooks/useTrackOnClick.ts +65 -0
  31. package/src/hooks/useTrackOnMount.test.tsx +79 -0
  32. package/src/hooks/useTrackOnMount.ts +24 -0
  33. package/src/hooks/useTrackOnVisible.test.tsx +313 -0
  34. package/src/hooks/useTrackOnVisible.ts +99 -0
  35. package/src/index.ts +4 -0
  36. package/src/next/NextFathomProvider.test.tsx +131 -0
  37. package/src/next/NextFathomProvider.tsx +62 -0
  38. package/src/next/NextFathomProviderApp.test.tsx +308 -0
  39. package/src/next/NextFathomProviderApp.tsx +106 -0
  40. package/src/next/NextFathomProviderPages.test.tsx +330 -0
  41. package/src/next/NextFathomProviderPages.tsx +112 -0
  42. package/src/next/compositions/withAppRouter.test.tsx +113 -0
  43. package/src/next/compositions/withAppRouter.tsx +48 -0
  44. package/src/next/compositions/withPagesRouter.test.tsx +113 -0
  45. package/src/next/compositions/withPagesRouter.tsx +44 -0
  46. package/src/next/index.ts +7 -0
  47. package/src/next/types.ts +19 -0
  48. package/src/types.ts +37 -0
  49. package/types/FathomContext.d.ts +3 -0
  50. package/types/FathomContext.d.ts.map +1 -0
  51. package/types/FathomProvider.d.ts +5 -0
  52. package/types/FathomProvider.d.ts.map +1 -0
  53. package/types/components/TrackClick.d.ts +39 -0
  54. package/types/components/TrackClick.d.ts.map +1 -0
  55. package/types/components/TrackPageview.d.ts +21 -0
  56. package/types/components/TrackPageview.d.ts.map +1 -0
  57. package/types/components/TrackVisible.d.ts +39 -0
  58. package/types/components/TrackVisible.d.ts.map +1 -0
  59. package/types/components/index.d.ts +4 -0
  60. package/types/components/index.d.ts.map +1 -0
  61. package/types/hooks/index.d.ts +5 -0
  62. package/types/hooks/index.d.ts.map +1 -0
  63. package/types/hooks/useFathom.d.ts +6 -0
  64. package/types/hooks/useFathom.d.ts.map +1 -0
  65. package/types/hooks/useTrackOnClick.d.ts +39 -0
  66. package/types/hooks/useTrackOnClick.d.ts.map +1 -0
  67. package/types/hooks/useTrackOnMount.d.ts +14 -0
  68. package/types/hooks/useTrackOnMount.d.ts.map +1 -0
  69. package/types/hooks/useTrackOnVisible.d.ts +43 -0
  70. package/types/hooks/useTrackOnVisible.d.ts.map +1 -0
  71. package/types/index.d.ts +5 -0
  72. package/types/index.d.ts.map +1 -0
  73. package/types/next/AppRouterProvider.d.ts +7 -0
  74. package/types/next/AppRouterProvider.d.ts.map +1 -0
  75. package/types/next/NextFathomProvider.d.ts +34 -0
  76. package/types/next/NextFathomProvider.d.ts.map +1 -0
  77. package/types/next/NextFathomProviderApp.d.ts +6 -0
  78. package/types/next/NextFathomProviderApp.d.ts.map +1 -0
  79. package/types/next/NextFathomProviderPages.d.ts +6 -0
  80. package/types/next/NextFathomProviderPages.d.ts.map +1 -0
  81. package/types/next/PagesRouterProvider.d.ts +7 -0
  82. package/types/next/PagesRouterProvider.d.ts.map +1 -0
  83. package/types/next/compositions/withAppRouter.d.ts +29 -0
  84. package/types/next/compositions/withAppRouter.d.ts.map +1 -0
  85. package/types/next/compositions/withPagesRouter.d.ts +25 -0
  86. package/types/next/compositions/withPagesRouter.d.ts.map +1 -0
  87. package/types/next/index.d.ts +6 -0
  88. package/types/next/index.d.ts.map +1 -0
  89. package/types/next/types.d.ts +16 -0
  90. package/types/next/types.d.ts.map +1 -0
  91. package/types/test-setup.d.ts +2 -0
  92. package/types/test-setup.d.ts.map +1 -0
  93. package/types/types.d.ts +34 -0
  94. package/types/types.d.ts.map +1 -0
  95. package/types/useFathom.d.ts +7 -0
  96. package/types/useFathom.d.ts.map +1 -0
@@ -0,0 +1,105 @@
1
+ import React, { useEffect, useRef, forwardRef, useCallback } from 'react'
2
+ import type { ElementType, ReactNode } from 'react'
3
+
4
+ import type { EventOptions } from 'fathom-client'
5
+
6
+ import { useFathom } from '../hooks/useFathom'
7
+
8
+ export interface TrackVisibleProps extends EventOptions {
9
+ /**
10
+ * Event name to track
11
+ */
12
+ eventName: string
13
+ /**
14
+ * Child element(s) to render
15
+ */
16
+ children?: ReactNode
17
+ /**
18
+ * Intersection observer options
19
+ */
20
+ observerOptions?: IntersectionObserverInit
21
+ /**
22
+ * Whether to track only once or every time it becomes visible
23
+ * @default true
24
+ */
25
+ trackOnce?: boolean
26
+ /**
27
+ * HTML element to render as wrapper
28
+ * @default 'div'
29
+ */
30
+ as?: ElementType
31
+ }
32
+
33
+ /**
34
+ * Component that tracks an event when it becomes visible in the viewport
35
+ *
36
+ * @example
37
+ * ```tsx
38
+ * <TrackVisible eventName="section-viewed" section="hero">
39
+ * <HeroSection />
40
+ * </TrackVisible>
41
+ * ```
42
+ */
43
+ export const TrackVisible = forwardRef<HTMLDivElement, TrackVisibleProps>(
44
+ function TrackVisible(
45
+ {
46
+ eventName,
47
+ children,
48
+ observerOptions,
49
+ trackOnce = true,
50
+ as: Component = 'div',
51
+ ...eventOptions
52
+ },
53
+ forwardedRef,
54
+ ) {
55
+ const { trackEvent } = useFathom()
56
+ const internalRef = useRef<HTMLDivElement | null>(null)
57
+ const hasTracked = useRef(false)
58
+
59
+ // Callback ref that handles both forwarded and internal refs
60
+ const setRef = useCallback(
61
+ (node: HTMLDivElement | null) => {
62
+ internalRef.current = node
63
+
64
+ if (typeof forwardedRef === 'function') {
65
+ forwardedRef(node)
66
+ } else if (forwardedRef !== null && forwardedRef !== undefined) {
67
+ forwardedRef.current = node
68
+ }
69
+ },
70
+ [forwardedRef],
71
+ )
72
+
73
+ useEffect(() => {
74
+ const element = internalRef.current
75
+ if (element === null) {
76
+ return
77
+ }
78
+
79
+ const observer = new IntersectionObserver(
80
+ (entries) => {
81
+ entries.forEach((entry) => {
82
+ if (entry.isIntersecting) {
83
+ if (!trackOnce || !hasTracked.current) {
84
+ trackEvent?.(eventName, eventOptions)
85
+ hasTracked.current = true
86
+ }
87
+ }
88
+ })
89
+ },
90
+ {
91
+ threshold: 0.1,
92
+ ...observerOptions,
93
+ },
94
+ )
95
+
96
+ observer.observe(element)
97
+
98
+ return () => {
99
+ observer.disconnect()
100
+ }
101
+ }, [eventName, eventOptions, observerOptions, trackOnce, trackEvent])
102
+
103
+ return <Component ref={setRef}>{children}</Component>
104
+ },
105
+ )
@@ -0,0 +1,3 @@
1
+ export * from './TrackClick'
2
+ export * from './TrackPageview'
3
+ export * from './TrackVisible'
@@ -0,0 +1,4 @@
1
+ export * from './useFathom'
2
+ export * from './useTrackOnMount'
3
+ export * from './useTrackOnClick'
4
+ export * from './useTrackOnVisible'
@@ -0,0 +1,51 @@
1
+ import React from 'react'
2
+
3
+ import { describe, expect, it, vi } from 'vitest'
4
+
5
+ import { renderHook } from '@testing-library/react'
6
+
7
+ import { FathomProvider } from '../FathomProvider'
8
+ import { useFathom } from './useFathom'
9
+
10
+ describe('useFathom', () => {
11
+ it('should return context values when used within FathomProvider', () => {
12
+ const mockClient = {
13
+ trackEvent: vi.fn(),
14
+ trackPageview: vi.fn(),
15
+ trackGoal: vi.fn(),
16
+ load: vi.fn(),
17
+ setSite: vi.fn(),
18
+ blockTrackingForMe: vi.fn(),
19
+ enableTrackingForMe: vi.fn(),
20
+ isTrackingEnabled: vi.fn(() => true),
21
+ }
22
+
23
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
24
+ <FathomProvider client={mockClient}>{children}</FathomProvider>
25
+ )
26
+
27
+ const { result } = renderHook(() => useFathom(), { wrapper })
28
+
29
+ expect(result.current.client).toBe(mockClient)
30
+ expect(result.current.trackEvent).toBeDefined()
31
+ expect(result.current.trackPageview).toBeDefined()
32
+ expect(result.current.trackGoal).toBeDefined()
33
+ expect(result.current.load).toBeDefined()
34
+ expect(result.current.setSite).toBeDefined()
35
+ expect(result.current.blockTrackingForMe).toBeDefined()
36
+ expect(result.current.enableTrackingForMe).toBeDefined()
37
+ expect(result.current.isTrackingEnabled).toBeDefined()
38
+ })
39
+
40
+ it('should return empty context when used outside FathomProvider', () => {
41
+ const { result } = renderHook(() => useFathom())
42
+
43
+ expect(result.current.client).toBeUndefined()
44
+ expect(result.current.trackEvent).toBeUndefined()
45
+ expect(result.current.trackPageview).toBeUndefined()
46
+ })
47
+
48
+ it('should have displayName', () => {
49
+ expect(useFathom.displayName).toBe('useFathom')
50
+ })
51
+ })
@@ -0,0 +1,11 @@
1
+ import { useContext } from 'react'
2
+
3
+ import { FathomContext } from '../FathomContext'
4
+ import type { FathomContextInterface } from '../types'
5
+
6
+ export const useFathom = (): FathomContextInterface => {
7
+ const context = useContext(FathomContext)
8
+ return context
9
+ }
10
+
11
+ useFathom.displayName = 'useFathom'
@@ -0,0 +1,197 @@
1
+ import React from 'react'
2
+
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
4
+
5
+ import { renderHook, fireEvent, render } from '@testing-library/react'
6
+
7
+ import { FathomProvider } from '../FathomProvider'
8
+ import { useTrackOnClick } from './useTrackOnClick'
9
+
10
+ describe('useTrackOnClick', () => {
11
+ const mockTrackEvent = vi.fn()
12
+ const mockClient = {
13
+ trackEvent: mockTrackEvent,
14
+ trackPageview: vi.fn(),
15
+ trackGoal: vi.fn(),
16
+ load: vi.fn(),
17
+ setSite: vi.fn(),
18
+ blockTrackingForMe: vi.fn(),
19
+ enableTrackingForMe: vi.fn(),
20
+ isTrackingEnabled: vi.fn(() => true),
21
+ }
22
+
23
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
24
+ <FathomProvider client={mockClient}>{children}</FathomProvider>
25
+ )
26
+
27
+ beforeEach(() => {
28
+ vi.clearAllMocks()
29
+ })
30
+
31
+ it('should return a click handler function', () => {
32
+ const { result } = renderHook(
33
+ () => useTrackOnClick({ eventName: 'test-event' }),
34
+ { wrapper },
35
+ )
36
+
37
+ expect(typeof result.current).toBe('function')
38
+ })
39
+
40
+ it('should track event when handler is called', () => {
41
+ const { result } = renderHook(
42
+ () => useTrackOnClick({ eventName: 'test-event' }),
43
+ { wrapper },
44
+ )
45
+
46
+ result.current()
47
+
48
+ expect(mockTrackEvent).toHaveBeenCalledTimes(1)
49
+ expect(mockTrackEvent).toHaveBeenCalledWith('test-event', {})
50
+ })
51
+
52
+ it('should track event with options', () => {
53
+ const { result } = renderHook(
54
+ () =>
55
+ useTrackOnClick({
56
+ eventName: 'test-event',
57
+ id: 'test-id',
58
+ value: 100,
59
+ }),
60
+ { wrapper },
61
+ )
62
+
63
+ result.current()
64
+
65
+ expect(mockTrackEvent).toHaveBeenCalledWith('test-event', {
66
+ id: 'test-id',
67
+ value: 100,
68
+ })
69
+ })
70
+
71
+ it('should prevent default when preventDefault is true', () => {
72
+ const TestComponent = () => {
73
+ const handleClick = useTrackOnClick({
74
+ eventName: 'test-event',
75
+ preventDefault: true,
76
+ })
77
+
78
+ return <button onClick={handleClick}>Click me</button>
79
+ }
80
+
81
+ const { getByText } = render(
82
+ <FathomProvider client={mockClient}>
83
+ <TestComponent />
84
+ </FathomProvider>,
85
+ )
86
+
87
+ const button = getByText('Click me')
88
+ const clickEvent = new MouseEvent('click', {
89
+ bubbles: true,
90
+ cancelable: true,
91
+ })
92
+ const preventDefaultSpy = vi.spyOn(clickEvent, 'preventDefault')
93
+
94
+ fireEvent(button, clickEvent)
95
+
96
+ expect(preventDefaultSpy).toHaveBeenCalled()
97
+ expect(mockTrackEvent).toHaveBeenCalledTimes(1)
98
+ })
99
+
100
+ it('should not prevent default when preventDefault is false', () => {
101
+ const TestComponent = () => {
102
+ const handleClick = useTrackOnClick({
103
+ eventName: 'test-event',
104
+ preventDefault: false,
105
+ })
106
+
107
+ return <button onClick={handleClick}>Click me</button>
108
+ }
109
+
110
+ const { getByText } = render(
111
+ <FathomProvider client={mockClient}>
112
+ <TestComponent />
113
+ </FathomProvider>,
114
+ )
115
+
116
+ const button = getByText('Click me')
117
+ const clickEvent = new MouseEvent('click', {
118
+ bubbles: true,
119
+ cancelable: true,
120
+ })
121
+ const preventDefaultSpy = vi.spyOn(clickEvent, 'preventDefault')
122
+
123
+ fireEvent(button, clickEvent)
124
+
125
+ expect(preventDefaultSpy).not.toHaveBeenCalled()
126
+ expect(mockTrackEvent).toHaveBeenCalledTimes(1)
127
+ })
128
+
129
+ it('should call custom callback after tracking', () => {
130
+ const callback = vi.fn()
131
+ const { result } = renderHook(
132
+ () =>
133
+ useTrackOnClick({
134
+ eventName: 'test-event',
135
+ callback,
136
+ }),
137
+ { wrapper },
138
+ )
139
+
140
+ const mockEvent = {} as React.MouseEvent
141
+ result.current(mockEvent)
142
+
143
+ expect(mockTrackEvent).toHaveBeenCalledTimes(1)
144
+ expect(callback).toHaveBeenCalledTimes(1)
145
+ expect(callback).toHaveBeenCalledWith(mockEvent)
146
+ })
147
+
148
+ it('should call callback even when event is undefined', () => {
149
+ const callback = vi.fn()
150
+ const { result } = renderHook(
151
+ () =>
152
+ useTrackOnClick({
153
+ eventName: 'test-event',
154
+ callback,
155
+ }),
156
+ { wrapper },
157
+ )
158
+
159
+ result.current()
160
+
161
+ expect(mockTrackEvent).toHaveBeenCalledTimes(1)
162
+ expect(callback).toHaveBeenCalledTimes(1)
163
+ expect(callback).toHaveBeenCalledWith(undefined)
164
+ })
165
+
166
+ it('should memoize the handler function', () => {
167
+ // Note: Due to eventOptions being created from destructuring,
168
+ // the handler will be recreated if eventOptions object reference changes.
169
+ // This test verifies that the handler is stable when options don't change.
170
+ const options = { eventName: 'test-event' }
171
+ const { result } = renderHook(() => useTrackOnClick(options), { wrapper })
172
+
173
+ const firstHandler = result.current
174
+ // Call the handler to ensure it works
175
+ firstHandler()
176
+
177
+ expect(mockTrackEvent).toHaveBeenCalledWith('test-event', {})
178
+ // The handler should be a function
179
+ expect(typeof firstHandler).toBe('function')
180
+ })
181
+
182
+ it('should create new handler when options change', () => {
183
+ const { result, rerender } = renderHook(
184
+ ({ eventName }) => useTrackOnClick({ eventName }),
185
+ {
186
+ wrapper,
187
+ initialProps: { eventName: 'event-1' },
188
+ },
189
+ )
190
+
191
+ const firstHandler = result.current
192
+ rerender({ eventName: 'event-2' })
193
+ const secondHandler = result.current
194
+
195
+ expect(firstHandler).not.toBe(secondHandler)
196
+ })
197
+ })
@@ -0,0 +1,65 @@
1
+ import { useCallback } from 'react'
2
+ import type { MouseEvent } from 'react'
3
+
4
+ import type { EventOptions } from 'fathom-client'
5
+
6
+ import { useFathom } from './useFathom'
7
+
8
+ export interface UseTrackOnClickOptions extends EventOptions {
9
+ /**
10
+ * Event name to track
11
+ */
12
+ eventName: string
13
+ /**
14
+ * Whether to prevent default behavior
15
+ * @default false
16
+ */
17
+ preventDefault?: boolean
18
+ /**
19
+ * Optional callback function to run after tracking
20
+ * Receives the click event as a parameter
21
+ */
22
+ callback?: (e?: MouseEvent) => void
23
+ }
24
+
25
+ /**
26
+ * Hook that returns a click handler function that tracks an event
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * function Button() {
31
+ * const handleClick = useTrackOnClick({
32
+ * eventName: 'button-click',
33
+ * id: 'signup-button',
34
+ * callback: (e) => {
35
+ * console.log('Button clicked!')
36
+ * // Your custom logic here
37
+ * },
38
+ * })
39
+ *
40
+ * return <button onClick={handleClick}>Sign Up</button>
41
+ * }
42
+ * ```
43
+ */
44
+ export const useTrackOnClick = (
45
+ options: UseTrackOnClickOptions,
46
+ ): ((e?: MouseEvent) => void) => {
47
+ const { trackEvent } = useFathom()
48
+ const {
49
+ eventName,
50
+ preventDefault = false,
51
+ callback,
52
+ ...eventOptions
53
+ } = options
54
+
55
+ return useCallback(
56
+ (e?: MouseEvent) => {
57
+ if (preventDefault && e) {
58
+ e.preventDefault()
59
+ }
60
+ trackEvent?.(eventName, eventOptions)
61
+ callback?.(e)
62
+ },
63
+ [eventName, preventDefault, trackEvent, eventOptions, callback],
64
+ )
65
+ }
@@ -0,0 +1,79 @@
1
+ import React from 'react'
2
+
3
+ import { beforeEach, describe, expect, it, vi } from 'vitest'
4
+
5
+ import { renderHook } from '@testing-library/react'
6
+
7
+ import { FathomProvider } from '../FathomProvider'
8
+ import { useTrackOnMount } from './useTrackOnMount'
9
+
10
+ describe('useTrackOnMount', () => {
11
+ const mockTrackPageview = vi.fn()
12
+ const mockClient = {
13
+ trackEvent: vi.fn(),
14
+ trackPageview: mockTrackPageview,
15
+ trackGoal: vi.fn(),
16
+ load: vi.fn(),
17
+ setSite: vi.fn(),
18
+ blockTrackingForMe: vi.fn(),
19
+ enableTrackingForMe: vi.fn(),
20
+ isTrackingEnabled: vi.fn(() => true),
21
+ }
22
+
23
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
24
+ <FathomProvider client={mockClient}>{children}</FathomProvider>
25
+ )
26
+
27
+ beforeEach(() => {
28
+ vi.clearAllMocks()
29
+ })
30
+
31
+ it('should track pageview on mount', () => {
32
+ renderHook(() => useTrackOnMount(), { wrapper })
33
+
34
+ expect(mockTrackPageview).toHaveBeenCalledTimes(1)
35
+ // The hook passes undefined, which becomes {} when merged
36
+ expect(mockTrackPageview).toHaveBeenCalled()
37
+ })
38
+
39
+ it('should track pageview with options', () => {
40
+ const options = { url: '/test-page' }
41
+ renderHook(() => useTrackOnMount(options), { wrapper })
42
+
43
+ expect(mockTrackPageview).toHaveBeenCalledTimes(1)
44
+ expect(mockTrackPageview).toHaveBeenCalledWith(options)
45
+ })
46
+
47
+ it('should only track once on mount', () => {
48
+ const { rerender } = renderHook(() => useTrackOnMount(), { wrapper })
49
+
50
+ expect(mockTrackPageview).toHaveBeenCalledTimes(1)
51
+
52
+ rerender()
53
+ expect(mockTrackPageview).toHaveBeenCalledTimes(1)
54
+ })
55
+
56
+ it('should not track when used outside FathomProvider', () => {
57
+ renderHook(() => useTrackOnMount())
58
+
59
+ expect(mockTrackPageview).not.toHaveBeenCalled()
60
+ })
61
+
62
+ it('should track with defaultPageviewOptions from provider', () => {
63
+ const defaultOptions = { url: '/default' }
64
+ const customWrapper = ({ children }: { children: React.ReactNode }) => (
65
+ <FathomProvider
66
+ client={mockClient}
67
+ defaultPageviewOptions={defaultOptions}
68
+ >
69
+ {children}
70
+ </FathomProvider>
71
+ )
72
+
73
+ renderHook(() => useTrackOnMount(), { wrapper: customWrapper })
74
+
75
+ expect(mockTrackPageview).toHaveBeenCalledTimes(1)
76
+ // The hook passes undefined, provider merges defaults internally
77
+ expect(mockTrackPageview).toHaveBeenCalledWith({ url: '/default' })
78
+ })
79
+ })
@@ -0,0 +1,24 @@
1
+ import { useEffect } from 'react'
2
+
3
+ import type { PageViewOptions } from 'fathom-client'
4
+
5
+ import { useFathom } from './useFathom'
6
+
7
+ /**
8
+ * Hook to track a pageview when a component mounts
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * function MyComponent() {
13
+ * useTrackOnMount({ url: '/custom-page' })
14
+ * return <div>Content</div>
15
+ * }
16
+ * ```
17
+ */
18
+ export const useTrackOnMount = (options?: PageViewOptions) => {
19
+ const { trackPageview } = useFathom()
20
+
21
+ useEffect(() => {
22
+ trackPageview?.(options)
23
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
24
+ }