@@ -10,6 +10,7 @@ import {
1010 useReducer ,
1111 useRef ,
1212} from "react" ;
13+ import { usePathname } from "next/navigation" ;
1314import type {
1415 ClientContext ,
1516 JavascriptTemplate ,
@@ -138,6 +139,24 @@ function InjectScript({
138139 return null ;
139140}
140141
142+ /** Merge scripts by templateId - later scripts override earlier ones */
143+ function mergeScriptsByTemplateId (
144+ scripts : TemplatizedScriptInsertion < unknown > [ ]
145+ ) : TemplatizedScriptInsertion < unknown > [ ] {
146+ const byTemplateId = new Map < string , TemplatizedScriptInsertion < unknown > > ( ) ;
147+ const nonTemplates : TemplatizedScriptInsertion < unknown > [ ] = [ ] ;
148+
149+ for ( const script of scripts ) {
150+ if ( script . type === "script-template" ) {
151+ byTemplateId . set ( script . templateId , script ) ;
152+ } else {
153+ nonTemplates . push ( script ) ;
154+ }
155+ }
156+
157+ return [ ...byTemplateId . values ( ) , ...nonTemplates ] ;
158+ }
159+
141160/** Renders initial scripts (from SSR) and dynamic scripts (from sendEvent calls) */
142161function NextlyticsScripts ( {
143162 initialScripts,
@@ -146,7 +165,7 @@ function NextlyticsScripts({
146165} ) {
147166 const context = useContext ( NextlyticsContext ) ;
148167 if ( ! context ) {
149- throw new Error ( "NextlyticsScripts should be called within NextlyticsContext" )
168+ throw new Error ( "NextlyticsScripts should be called within NextlyticsContext" ) ;
150169 }
151170
152171 const { scriptsRef, subscribersRef, templates, requestId } = context ;
@@ -161,20 +180,22 @@ function NextlyticsScripts({
161180 } , [ subscribersRef ] ) ;
162181
163182 const dynamicScripts = scriptsRef . current ;
164- const allScripts = [ ...initialScripts , ...dynamicScripts ] ;
183+ // Merge by templateId - dynamic scripts override initial (they have newer params)
184+ const allScripts = mergeScriptsByTemplateId ( [ ...initialScripts , ...dynamicScripts ] ) ;
165185
166186 return (
167187 < >
168- { allScripts . flatMap ( ( script , scriptIndex ) => {
188+ { allScripts . flatMap ( ( script ) => {
169189 if ( script . type !== "script-template" ) return [ ] ;
170190 const template = templates [ script . templateId ] ;
171191 if ( ! template ) {
172192 console . warn ( `[Nextlytics] Template "${ script . templateId } " not found` ) ;
173193 return [ ] ;
174194 }
195+ // Use templateId as key - same template = same component instances
175196 return template . items . map ( ( item , itemIndex ) => (
176197 < InjectScript
177- key = { `${ scriptIndex } : ${ script . templateId } :${ itemIndex } ` }
198+ key = { `${ script . templateId } :${ itemIndex } ` }
178199 item = { item }
179200 params = { script . params }
180201 requestId = { requestId }
@@ -224,6 +245,11 @@ async function sendEventToServer(
224245export function NextlyticsClient ( props : { ctx : NextlyticsContext ; children ?: ReactNode } ) {
225246 const { requestId, scripts : initialScripts = [ ] , templates = { } } = props . ctx ;
226247
248+ // Track pathname for soft navigation detection
249+ const pathname = usePathname ( ) ;
250+ const initialPathRef = useRef < string | null > ( null ) ;
251+ const lastPathRef = useRef < string | null > ( null ) ;
252+
227253 // Refs for dynamic scripts (from sendEvent calls) - stable, no re-renders
228254 const scriptsRef = useRef < TemplatizedScriptInsertion < unknown > [ ] > ( [ ] ) ;
229255 const subscribersRef = useRef < Set < ( ) => void > > ( new Set ( ) ) ;
@@ -241,11 +267,27 @@ export function NextlyticsClient(props: { ctx: NextlyticsContext; children?: Rea
241267
242268 // Send client-init on mount (once per requestId)
243269 useEffect ( ( ) => {
270+ initialPathRef . current = pathname ;
271+ lastPathRef . current = pathname ;
244272 const clientContext = createClientContext ( ) ;
245273 sendEventToServer ( requestId , "client-init" , clientContext ) . then ( ( { scripts } ) => {
246274 if ( scripts ?. length ) addScripts ( scripts ) ;
247275 } ) ;
248- } , [ requestId , addScripts ] ) ;
276+ } , [ requestId , addScripts , pathname ] ) ;
277+
278+ // Detect soft navigation and fetch scripts for new page
279+ useEffect ( ( ) => {
280+ // Skip if this is the initial render or same path
281+ if ( initialPathRef . current === null || pathname === lastPathRef . current ) {
282+ return ;
283+ }
284+ lastPathRef . current = pathname ;
285+
286+ const clientContext = createClientContext ( ) ;
287+ sendEventToServer ( requestId , "soft-navigation" , clientContext ) . then ( ( { scripts } ) => {
288+ if ( scripts ?. length ) addScripts ( scripts ) ;
289+ } ) ;
290+ } , [ pathname , requestId , addScripts ] ) ;
249291
250292 return (
251293 < NextlyticsContext . Provider value = { contextValue } >
0 commit comments