@nosto/nosto-react 2.11.0 → 2.12.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.
package/dist/index.d.ts CHANGED
@@ -437,7 +437,43 @@ export declare type ToCamelCase<T> = T extends (infer U)[] ? ToCamelCase<U>[] :
437
437
  } : T;
438
438
 
439
439
  /**
440
- * You can personalise your cart and checkout pages by using the `useNosto404` hook.
440
+ * You can personalise your 404 error pages by using the `useNosto404` hook.
441
+ *
442
+ * @example Basic 404 page usage
443
+ * ```tsx
444
+ * import { useNosto404 } from '@nosto/nosto-react'
445
+ *
446
+ * function NotFoundPage() {
447
+ * useNosto404({
448
+ * placements: ['notfound-nosto-1', 'notfound-popular-products']
449
+ * })
450
+ *
451
+ * return (
452
+ * <div>
453
+ * <h1>Page Not Found</h1>
454
+ * <p>Sorry, the page you're looking for doesn't exist.</p>
455
+ * <div id="notfound-popular-products" />
456
+ * <div id="notfound-nosto-1" />
457
+ * </div>
458
+ * )
459
+ * }
460
+ * ```
461
+ *
462
+ * @example 404 page with default placements
463
+ * ```tsx
464
+ * import { useNosto404 } from '@nosto/nosto-react'
465
+ *
466
+ * function Simple404Page() {
467
+ * useNosto404() // Uses all available placements
468
+ *
469
+ * return (
470
+ * <div>
471
+ * <h1>Oops! Page not found</h1>
472
+ * <p>Let us help you find what you're looking for:</p>
473
+ * </div>
474
+ * )
475
+ * }
476
+ * ```
441
477
  *
442
478
  * @group Hooks
443
479
  */
@@ -446,6 +482,47 @@ export declare function useNosto404(props?: Nosto404Props): void;
446
482
  /**
447
483
  * You can personalise your category and collection pages by using the useNostoCategory hook.
448
484
  *
485
+ * @example Basic category page usage
486
+ * ```tsx
487
+ * import { useNostoCategory } from '@nosto/nosto-react'
488
+ *
489
+ * function CategoryPage({ categoryName }: { categoryName: string }) {
490
+ * useNostoCategory({
491
+ * category: categoryName,
492
+ * placements: ['categorypage-nosto-1', 'categorypage-nosto-2']
493
+ * })
494
+ *
495
+ * return (
496
+ * <div>
497
+ * <h1>Category: {categoryName}</h1>
498
+ * <div id="categorypage-nosto-1" />
499
+ * <div id="categorypage-nosto-2" />
500
+ * </div>
501
+ * )
502
+ * }
503
+ * ```
504
+ *
505
+ * @example Collection page with custom placements
506
+ * ```tsx
507
+ * import { useNostoCategory } from '@nosto/nosto-react'
508
+ *
509
+ * function CollectionPage({ collection }: { collection: { name: string, id: string } }) {
510
+ * useNostoCategory({
511
+ * category: `collection-${collection.id}`,
512
+ * placements: ['collection-banner', 'collection-recommendations']
513
+ * })
514
+ *
515
+ * return (
516
+ * <div>
517
+ * <h1>{collection.name} Collection</h1>
518
+ * <div id="collection-banner" />
519
+ * {/\* Product grid here *\/}
520
+ * <div id="collection-recommendations" />
521
+ * </div>
522
+ * )
523
+ * }
524
+ * ```
525
+ *
449
526
  * @group Hooks
450
527
  */
451
528
  export declare function useNostoCategory({ category, placements }: NostoCategoryProps): void;
@@ -453,6 +530,46 @@ export declare function useNostoCategory({ category, placements }: NostoCategory
453
530
  /**
454
531
  * You can personalise your cart and checkout pages by using the useNostoCheckout hook.
455
532
  *
533
+ * @example Basic cart page usage
534
+ * ```tsx
535
+ * import { useNostoCheckout } from '@nosto/nosto-react'
536
+ *
537
+ * function CartPage() {
538
+ * useNostoCheckout({
539
+ * placements: ['cartpage-nosto-1', 'cartpage-cross-sell']
540
+ * })
541
+ *
542
+ * return (
543
+ * <div>
544
+ * <h1>Your Cart</h1>
545
+ * {/\* Cart items here *\/}
546
+ * <div id="cartpage-cross-sell" />
547
+ * <div id="cartpage-nosto-1" />
548
+ * </div>
549
+ * )
550
+ * }
551
+ * ```
552
+ *
553
+ * @example Checkout page with recommendations
554
+ * ```tsx
555
+ * import { useNostoCheckout } from '@nosto/nosto-react'
556
+ *
557
+ * function CheckoutPage() {
558
+ * useNostoCheckout({
559
+ * placements: ['checkout-upsell', 'checkout-last-chance']
560
+ * })
561
+ *
562
+ * return (
563
+ * <div>
564
+ * <h1>Checkout</h1>
565
+ * <div id="checkout-upsell" />
566
+ * {/\* Checkout form here *\/}
567
+ * <div id="checkout-last-chance" />
568
+ * </div>
569
+ * )
570
+ * }
571
+ * ```
572
+ *
456
573
  * @group Hooks
457
574
  */
458
575
  export declare function useNostoCheckout(props?: NostoCheckoutProps): void;
@@ -460,13 +577,88 @@ export declare function useNostoCheckout(props?: NostoCheckoutProps): void;
460
577
  /**
461
578
  * A hook that allows you to access the NostoContext and retrieve Nosto-related data from it in React components.
462
579
  *
463
- * @group Essential Functions
580
+ * @example
581
+ * ```tsx
582
+ * import { useNostoContext } from '@nosto/nosto-react'
583
+ *
584
+ * function NostoStatus() {
585
+ * const { clientScriptLoaded, account, responseMode } = useNostoContext()
586
+ *
587
+ * return (
588
+ * <div>
589
+ * <p>Account: {account}</p>
590
+ * <p>Script loaded: {String(clientScriptLoaded)}</p>
591
+ * <p>Response mode: {responseMode}</p>
592
+ * </div>
593
+ * )
594
+ * }
595
+ * ```
596
+ *
597
+ * @example Using context for conditional rendering
598
+ * ```tsx
599
+ * import { useNostoContext } from '@nosto/nosto-react'
600
+ *
601
+ * function ConditionalRecommendations() {
602
+ * const { clientScriptLoaded, recommendationComponent } = useNostoContext()
603
+ *
604
+ * if (!clientScriptLoaded) {
605
+ * return <div>Loading recommendations...</div>
606
+ * }
607
+ *
608
+ * return (
609
+ * <div>
610
+ * {recommendationComponent && (
611
+ * <div id="nosto-recommendation-placeholder" />
612
+ * )}
613
+ * </div>
614
+ * )
615
+ * }
616
+ * ```
617
+ *
618
+ * @group Hooks
464
619
  */
465
620
  export declare function useNostoContext(): NostoContextType;
466
621
 
467
622
  /**
468
623
  * You can personalise your home page by using the useNostoHome hook.
469
624
  *
625
+ * @example Basic home page usage
626
+ * ```tsx
627
+ * import { useNostoHome } from '@nosto/nosto-react'
628
+ *
629
+ * function HomePage() {
630
+ * useNostoHome({
631
+ * placements: ['frontpage-nosto-1', 'frontpage-nosto-2', 'frontpage-hero']
632
+ * })
633
+ *
634
+ * return (
635
+ * <div>
636
+ * <div id="frontpage-hero" />
637
+ * <h1>Welcome to our store</h1>
638
+ * <div id="frontpage-nosto-1" />
639
+ * <div id="frontpage-nosto-2" />
640
+ * </div>
641
+ * )
642
+ * }
643
+ * ```
644
+ *
645
+ * @example Home page with default placements
646
+ * ```tsx
647
+ * import { useNostoHome } from '@nosto/nosto-react'
648
+ *
649
+ * function SimpleHomePage() {
650
+ * // Uses all available placements configured in Nosto admin
651
+ * useNostoHome()
652
+ *
653
+ * return (
654
+ * <div>
655
+ * <h1>Home Page</h1>
656
+ * {/\* Nosto will inject content into configured placements *\/}
657
+ * </div>
658
+ * )
659
+ * }
660
+ * ```
661
+ *
470
662
  * @group Hooks
471
663
  */
472
664
  export declare function useNostoHome(props?: NostoHomeProps): void;
@@ -474,6 +666,57 @@ export declare function useNostoHome(props?: NostoHomeProps): void;
474
666
  /**
475
667
  * You can personalise your order-confirmation/thank-you page by using the `useNostoOrder` hook.
476
668
  *
669
+ * @example Basic order confirmation page usage
670
+ * ```tsx
671
+ * import { useNostoOrder } from '@nosto/nosto-react'
672
+ *
673
+ * function OrderConfirmationPage({ order }: { order: Order }) {
674
+ * useNostoOrder({
675
+ * order: order,
676
+ * placements: ['orderconfirm-nosto-1', 'orderconfirm-cross-sell']
677
+ * })
678
+ *
679
+ * return (
680
+ * <div>
681
+ * <h1>Order Confirmed!</h1>
682
+ * <p>Order #{order.order_number}</p>
683
+ * <div id="orderconfirm-cross-sell" />
684
+ * <div id="orderconfirm-nosto-1" />
685
+ * </div>
686
+ * )
687
+ * }
688
+ * ```
689
+ *
690
+ * @example Complete order with camelCase conversion
691
+ * ```tsx
692
+ * import { useNostoOrder } from '@nosto/nosto-react'
693
+ *
694
+ * function ThankYouPage({ orderData }: { orderData: ToCamelCase<Order> }) {
695
+ * useNostoOrder({
696
+ * order: {
697
+ * orderNumber: orderData.orderNumber,
698
+ * customerEmail: orderData.customerEmail,
699
+ * purchasedItems: orderData.purchasedItems.map(item => ({
700
+ * productId: item.productId,
701
+ * skuId: item.skuId,
702
+ * name: item.name,
703
+ * unitPrice: item.unitPrice,
704
+ * quantity: item.quantity
705
+ * }))
706
+ * },
707
+ * placements: ['thankyou-recommendations', 'thankyou-reviews']
708
+ * })
709
+ *
710
+ * return (
711
+ * <div>
712
+ * <h1>Thank you for your purchase!</h1>
713
+ * <div id="thankyou-recommendations" />
714
+ * <div id="thankyou-reviews" />
715
+ * </div>
716
+ * )
717
+ * }
718
+ * ```
719
+ *
477
720
  * @group Hooks
478
721
  */
479
722
  export declare function useNostoOrder({ order, placements }: NostoOrderProps): void;
@@ -481,6 +724,64 @@ export declare function useNostoOrder({ order, placements }: NostoOrderProps): v
481
724
  /**
482
725
  * You can personalise your miscellaneous pages by using the useNostoOther hook.
483
726
  *
727
+ * @example Basic usage for contact page
728
+ * ```tsx
729
+ * import { useNostoOther } from '@nosto/nosto-react'
730
+ *
731
+ * function ContactPage() {
732
+ * useNostoOther({
733
+ * placements: ['contactpage-nosto-1', 'contactpage-popular-products']
734
+ * })
735
+ *
736
+ * return (
737
+ * <div>
738
+ * <h1>Contact Us</h1>
739
+ * <form>
740
+ * {/\* Contact form fields *\/}
741
+ * </form>
742
+ * <div id="contactpage-popular-products" />
743
+ * <div id="contactpage-nosto-1" />
744
+ * </div>
745
+ * )
746
+ * }
747
+ * ```
748
+ *
749
+ * @example About page with recommendations
750
+ * ```tsx
751
+ * import { useNostoOther } from '@nosto/nosto-react'
752
+ *
753
+ * function AboutPage() {
754
+ * useNostoOther({
755
+ * placements: ['aboutpage-featured', 'aboutpage-bestsellers']
756
+ * })
757
+ *
758
+ * return (
759
+ * <div>
760
+ * <h1>About Our Company</h1>
761
+ * <p>Learn more about our story...</p>
762
+ * <div id="aboutpage-featured" />
763
+ * <div id="aboutpage-bestsellers" />
764
+ * </div>
765
+ * )
766
+ * }
767
+ * ```
768
+ *
769
+ * @example Default placements for any other page
770
+ * ```tsx
771
+ * import { useNostoOther } from '@nosto/nosto-react'
772
+ *
773
+ * function GenericPage() {
774
+ * useNostoOther() // Uses all available placements
775
+ *
776
+ * return (
777
+ * <div>
778
+ * <h1>Generic Page</h1>
779
+ * <p>This could be any page type not covered by other hooks.</p>
780
+ * </div>
781
+ * )
782
+ * }
783
+ * ```
784
+ *
484
785
  * @group Hooks
485
786
  */
486
787
  export declare function useNostoOther(props?: NostoOtherProps): void;
@@ -488,6 +789,58 @@ export declare function useNostoOther(props?: NostoOtherProps): void;
488
789
  /**
489
790
  * You can personalise your product pages by using the useNostoProduct hook.
490
791
  *
792
+ * @example Basic product page usage
793
+ * ```tsx
794
+ * import { useNostoProduct } from '@nosto/nosto-react'
795
+ *
796
+ * function ProductPage({ productId }: { productId: string }) {
797
+ * useNostoProduct({
798
+ * product: productId,
799
+ * placements: ['productpage-nosto-1', 'productpage-nosto-2']
800
+ * })
801
+ *
802
+ * return (
803
+ * <div>
804
+ * <h1>Product Details</h1>
805
+ * <div id="productpage-nosto-1" />
806
+ * <div id="productpage-nosto-2" />
807
+ * </div>
808
+ * )
809
+ * }
810
+ * ```
811
+ *
812
+ * @example Advanced product page with full tagging
813
+ * ```tsx
814
+ * import { useNostoProduct } from '@nosto/nosto-react'
815
+ *
816
+ * function AdvancedProductPage({ product }: { product: Product }) {
817
+ * useNostoProduct({
818
+ * product: product.id,
819
+ * reference: `product-${product.id}`,
820
+ * tagging: {
821
+ * product_id: product.id,
822
+ * name: product.name,
823
+ * url: product.url,
824
+ * price: product.price,
825
+ * list_price: product.listPrice,
826
+ * image_url: product.imageUrl,
827
+ * categories: product.categories,
828
+ * brand: product.brand,
829
+ * selected_sku_id: product.selectedVariant?.id
830
+ * },
831
+ * placements: ['productpage-cross-sell', 'productpage-upsell']
832
+ * })
833
+ *
834
+ * return (
835
+ * <div>
836
+ * <h1>{product.name}</h1>
837
+ * <div id="productpage-cross-sell" />
838
+ * <div id="productpage-upsell" />
839
+ * </div>
840
+ * )
841
+ * }
842
+ * ```
843
+ *
491
844
  * @group Hooks
492
845
  */
493
846
  export declare function useNostoProduct({ product, tagging, placements, reference }: NostoProductProps): void;
@@ -495,6 +848,55 @@ export declare function useNostoProduct({ product, tagging, placements, referenc
495
848
  /**
496
849
  * You can personalise your search pages by using the useNostoSearch hook.
497
850
  *
851
+ * @example Basic search page usage
852
+ * ```tsx
853
+ * import { useNostoSearch } from '@nosto/nosto-react'
854
+ *
855
+ * function SearchPage({ searchQuery }: { searchQuery: string }) {
856
+ * useNostoSearch({
857
+ * query: searchQuery,
858
+ * placements: ['searchpage-nosto-1', 'searchpage-no-results']
859
+ * })
860
+ *
861
+ * return (
862
+ * <div>
863
+ * <h1>Search Results for "{searchQuery}"</h1>
864
+ * {/\* Search results here *\/}
865
+ * <div id="searchpage-nosto-1" />
866
+ * <div id="searchpage-no-results" />
867
+ * </div>
868
+ * )
869
+ * }
870
+ * ```
871
+ *
872
+ * @example Search with dynamic query updates
873
+ * ```tsx
874
+ * import { useNostoSearch } from '@nosto/nosto-react'
875
+ * import { useState } from 'react'
876
+ *
877
+ * function SearchPageWithFilters() {
878
+ * const [query, setQuery] = useState('')
879
+ *
880
+ * useNostoSearch({
881
+ * query: query,
882
+ * placements: ['search-recommendations', 'search-trending']
883
+ * })
884
+ *
885
+ * return (
886
+ * <div>
887
+ * <input
888
+ * type="text"
889
+ * value={query}
890
+ * onChange={(e) => setQuery(e.target.value)}
891
+ * placeholder="Search products..."
892
+ * />
893
+ * <div id="search-recommendations" />
894
+ * <div id="search-trending" />
895
+ * </div>
896
+ * )
897
+ * }
898
+ * ```
899
+ *
498
900
  * @group Hooks
499
901
  */
500
902
  export declare function useNostoSearch({ query, placements }: NostoSearchProps): void;
@@ -502,6 +904,62 @@ export declare function useNostoSearch({ query, placements }: NostoSearchProps):
502
904
  /**
503
905
  * Nosto React requires that you pass it the details of current cart contents and the details of the currently logged-in customer, if any, on every route change.
504
906
  *
907
+ * @example Basic session management
908
+ * ```tsx
909
+ * import { useNostoSession } from '@nosto/nosto-react'
910
+ *
911
+ * function App({ cart, user }: { cart?: Cart, user?: Customer }) {
912
+ * useNostoSession({
913
+ * cart: cart ? {
914
+ * items: cart.items.map(item => ({
915
+ * product_id: item.productId,
916
+ * sku_id: item.skuId,
917
+ * name: item.name,
918
+ * unit_price: item.price,
919
+ * quantity: item.quantity
920
+ * }))
921
+ * } : undefined,
922
+ * customer: user ? {
923
+ * customer_reference: user.id,
924
+ * email: user.email,
925
+ * first_name: user.firstName,
926
+ * last_name: user.lastName
927
+ * } : undefined
928
+ * })
929
+ *
930
+ * return <div>App content</div>
931
+ * }
932
+ * ```
933
+ *
934
+ * @example Route-based session updates
935
+ * ```tsx
936
+ * import { useNostoSession } from '@nosto/nosto-react'
937
+ * import { useEffect, useState } from 'react'
938
+ *
939
+ * function SessionProvider({ children }: { children: React.ReactNode }) {
940
+ * const [cart, setCart] = useState<Cart | null>(null)
941
+ * const [customer, setCustomer] = useState<Customer | null>(null)
942
+ *
943
+ * useEffect(() => {
944
+ * // Load cart and customer data
945
+ * loadCartData().then(setCart)
946
+ * loadCustomerData().then(setCustomer)
947
+ * }, [])
948
+ *
949
+ * useNostoSession({
950
+ * cart: cart ? {
951
+ * items: cart.items
952
+ * } : undefined,
953
+ * customer: customer ? {
954
+ * customerReference: customer.id,
955
+ * email: customer.email
956
+ * } : undefined
957
+ * })
958
+ *
959
+ * return <>{children}</>
960
+ * }
961
+ * ```
962
+ *
505
963
  * @group Hooks
506
964
  */
507
965
  export declare function useNostoSession({ cart, customer }?: NostoSessionProps): void;
package/dist/index.es.js CHANGED
@@ -97,7 +97,6 @@ function d(e, t, n) {
97
97
  }
98
98
  function F(e) {
99
99
  return x(e.recommendationComponent, {
100
- // eslint-disable-next-line react/prop-types
101
100
  nostoRecommendation: e.nostoRecommendation
102
101
  });
103
102
  }
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@nosto/nosto-react",
3
3
  "description": "Component library to simply implementing Nosto on React.",
4
- "version": "2.11.0",
4
+ "version": "2.12.0",
5
5
  "author": "Mridang Agarwalla, Dominik Gilg",
6
6
  "license": "ISC",
7
+ "type": "module",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "git+https://github.com/Nosto/nosto-react.git"
@@ -48,8 +49,8 @@
48
49
  "@types/react": "^19.0.2",
49
50
  "@types/react-dom": "^19.0.0",
50
51
  "@types/user-event": "^4.1.1",
51
- "@vitejs/plugin-react": "^5.0.0",
52
- "@vitest/coverage-v8": "^3.0.9",
52
+ "@vitejs/plugin-react": "^5.1.1",
53
+ "@vitest/coverage-v8": "^4.0.10",
53
54
  "eslint": "^9.13.0",
54
55
  "eslint-plugin-barrel-files": "^3.0.1",
55
56
  "eslint-plugin-promise": "^7.1.0",
@@ -66,18 +67,18 @@
66
67
  "typedoc": "^0.28.9",
67
68
  "typescript": "^5.9.2",
68
69
  "typescript-eslint": "^8.39.0",
69
- "vite": "^7.0.2",
70
+ "vite": "^7.2.2",
70
71
  "vite-plugin-dts": "^4.2.2",
71
- "vitest": "^3.0.2"
72
+ "vitest": "^4.0.10"
72
73
  },
73
74
  "main": "./dist/index.umd.js",
74
75
  "module": "./dist/index.es.js",
75
76
  "types": "./dist/index.d.ts",
76
77
  "exports": {
77
78
  ".": {
79
+ "types": "./dist/index.d.ts",
78
80
  "import": "./dist/index.es.js",
79
- "require": "./dist/index.umd.js",
80
- "types": "./dist/index.d.ts"
81
+ "require": "./dist/index.umd.js"
81
82
  }
82
83
  },
83
84
  "bugs": {
@@ -1,6 +1,47 @@
1
1
  import { useEffect, useRef, useMemo, type EffectCallback, type DependencyList } from "react"
2
2
  import { deepCompare } from "../utils/compare"
3
3
 
4
+ /**
5
+ * Similar to useEffect but performs deep comparison of dependencies instead of shallow comparison.
6
+ * Useful when dependencies are objects or arrays that may be recreated on each render.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import { useDeepCompareEffect } from '@nosto/nosto-react'
11
+ *
12
+ * function MyComponent({ user }: { user: { id: string, preferences: string[] } }) {
13
+ * useDeepCompareEffect(() => {
14
+ * console.log('User preferences changed:', user.preferences)
15
+ * // This will only run when user object actually changes,
16
+ * // not when it's recreated with same values
17
+ * }, [user])
18
+ *
19
+ * return <div>User: {user.id}</div>
20
+ * }
21
+ * ```
22
+ *
23
+ * @example Comparing arrays and objects
24
+ * ```tsx
25
+ * import { useDeepCompareEffect } from '@nosto/nosto-react'
26
+ *
27
+ * function ProductList({ filters, sortOptions }: {
28
+ * filters: { category: string, price: { min: number, max: number } }
29
+ * sortOptions: string[]
30
+ * }) {
31
+ * useDeepCompareEffect(() => {
32
+ * // This effect will only run when filters or sortOptions actually change
33
+ * fetchProducts(filters, sortOptions)
34
+ * }, [filters, sortOptions])
35
+ *
36
+ * return <div>Product list</div>
37
+ * }
38
+ * ```
39
+ *
40
+ * @param callback The effect callback function
41
+ * @param dependencies Array of dependencies to deep compare
42
+ *
43
+ * @group Hooks
44
+ */
4
45
  export function useDeepCompareEffect(callback: EffectCallback, dependencies: DependencyList) {
5
46
  return useEffect(callback, useDeepCompareMemoize(dependencies))
6
47
  }
@@ -7,6 +7,59 @@ type NostoScriptProps = Pick<NostoProviderProps, "account" | "host" | "shopifyMa
7
7
 
8
8
  const defaultAttributes = { "nosto-client-script": "" }
9
9
 
10
+ /**
11
+ * Hook for loading the Nosto client script and managing its load state.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * import { useLoadClientScript } from '@nosto/nosto-react'
16
+ *
17
+ * function MyNostoProvider() {
18
+ * const { clientScriptLoaded } = useLoadClientScript({
19
+ * account: 'shopify-123456',
20
+ * loadScript: true,
21
+ * shopifyMarkets: {
22
+ * marketId: 'US',
23
+ * language: 'en'
24
+ * }
25
+ * })
26
+ *
27
+ * return (
28
+ * <div>
29
+ * {clientScriptLoaded ? (
30
+ * <p>Nosto script loaded successfully</p>
31
+ * ) : (
32
+ * <p>Loading Nosto script...</p>
33
+ * )}
34
+ * </div>
35
+ * )
36
+ * }
37
+ * ```
38
+ *
39
+ * @example Custom script loader
40
+ * ```tsx
41
+ * import { useLoadClientScript } from '@nosto/nosto-react'
42
+ *
43
+ * function CustomNostoProvider() {
44
+ * const customScriptLoader = (url: string) => {
45
+ * const script = document.createElement('script')
46
+ * script.src = url
47
+ * script.async = true
48
+ * document.head.appendChild(script)
49
+ * return script
50
+ * }
51
+ *
52
+ * const { clientScriptLoaded } = useLoadClientScript({
53
+ * account: 'my-nosto-account',
54
+ * scriptLoader: customScriptLoader
55
+ * })
56
+ *
57
+ * return <div>Script loaded: {String(clientScriptLoaded)}</div>
58
+ * }
59
+ * ```
60
+ *
61
+ * @group Hooks
62
+ */
10
63
  export function useLoadClientScript(props: NostoScriptProps) {
11
64
  const {
12
65
  scriptLoader = scriptLoaderFn,
@@ -7,7 +7,43 @@ import { useRenderCampaigns } from "./useRenderCampaigns"
7
7
  export type Nosto404Props = { placements?: string[] }
8
8
 
9
9
  /**
10
- * You can personalise your cart and checkout pages by using the `useNosto404` hook.
10
+ * You can personalise your 404 error pages by using the `useNosto404` hook.
11
+ *
12
+ * @example Basic 404 page usage
13
+ * ```tsx
14
+ * import { useNosto404 } from '@nosto/nosto-react'
15
+ *
16
+ * function NotFoundPage() {
17
+ * useNosto404({
18
+ * placements: ['notfound-nosto-1', 'notfound-popular-products']
19
+ * })
20
+ *
21
+ * return (
22
+ * <div>
23
+ * <h1>Page Not Found</h1>
24
+ * <p>Sorry, the page you're looking for doesn't exist.</p>
25
+ * <div id="notfound-popular-products" />
26
+ * <div id="notfound-nosto-1" />
27
+ * </div>
28
+ * )
29
+ * }
30
+ * ```
31
+ *
32
+ * @example 404 page with default placements
33
+ * ```tsx
34
+ * import { useNosto404 } from '@nosto/nosto-react'
35
+ *
36
+ * function Simple404Page() {
37
+ * useNosto404() // Uses all available placements
38
+ *
39
+ * return (
40
+ * <div>
41
+ * <h1>Oops! Page not found</h1>
42
+ * <p>Let us help you find what you're looking for:</p>
43
+ * </div>
44
+ * )
45
+ * }
46
+ * ```
11
47
  *
12
48
  * @group Hooks
13
49
  */
@@ -4,6 +4,51 @@ import { useDeepCompareEffect } from "./useDeepCompareEffect"
4
4
  import { nostojs } from "@nosto/nosto-js"
5
5
  import { API } from "@nosto/nosto-js/client"
6
6
 
7
+ /**
8
+ * Hook for executing code that requires access to the Nosto API.
9
+ * Waits for the client script to load before executing the callback.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * import { useNostoApi } from '@nosto/nosto-react'
14
+ *
15
+ * function ProductRecommendations({ productId }: { productId: string }) {
16
+ * useNostoApi(async (api) => {
17
+ * const data = await api
18
+ * .defaultSession()
19
+ * .viewProduct(productId)
20
+ * .setPlacements(['productpage-nosto-1', 'productpage-nosto-2'])
21
+ * .load()
22
+ *
23
+ * console.log('Recommendations loaded:', data)
24
+ * }, [productId])
25
+ *
26
+ * return <div>Product recommendations will appear here</div>
27
+ * }
28
+ * ```
29
+ *
30
+ * @example Using deep comparison for complex dependencies
31
+ * ```tsx
32
+ * import { useNostoApi } from '@nosto/nosto-react'
33
+ *
34
+ * function OrderThankYou({ order }: { order: Order }) {
35
+ * useNostoApi(async (api) => {
36
+ * await api
37
+ * .defaultSession()
38
+ * .addOrder(order)
39
+ * .load()
40
+ * }, [order], { deep: true })
41
+ *
42
+ * return <div>Thank you for your order!</div>
43
+ * }
44
+ * ```
45
+ *
46
+ * @param cb Callback function that receives the Nosto API instance
47
+ * @param deps Optional dependency list for useEffect
48
+ * @param flags Optional flags, including `deep` for deep comparison of dependencies
49
+ *
50
+ * @group Hooks
51
+ */
7
52
  export function useNostoApi(cb: (api: API) => void, deps?: DependencyList, flags?: { deep?: boolean }): void {
8
53
  const { clientScriptLoaded } = useNostoContext()
9
54
  const useEffectFn = flags?.deep ? useDeepCompareEffect : useEffect
@@ -12,6 +12,47 @@ export type NostoCategoryProps = {
12
12
  /**
13
13
  * You can personalise your category and collection pages by using the useNostoCategory hook.
14
14
  *
15
+ * @example Basic category page usage
16
+ * ```tsx
17
+ * import { useNostoCategory } from '@nosto/nosto-react'
18
+ *
19
+ * function CategoryPage({ categoryName }: { categoryName: string }) {
20
+ * useNostoCategory({
21
+ * category: categoryName,
22
+ * placements: ['categorypage-nosto-1', 'categorypage-nosto-2']
23
+ * })
24
+ *
25
+ * return (
26
+ * <div>
27
+ * <h1>Category: {categoryName}</h1>
28
+ * <div id="categorypage-nosto-1" />
29
+ * <div id="categorypage-nosto-2" />
30
+ * </div>
31
+ * )
32
+ * }
33
+ * ```
34
+ *
35
+ * @example Collection page with custom placements
36
+ * ```tsx
37
+ * import { useNostoCategory } from '@nosto/nosto-react'
38
+ *
39
+ * function CollectionPage({ collection }: { collection: { name: string, id: string } }) {
40
+ * useNostoCategory({
41
+ * category: `collection-${collection.id}`,
42
+ * placements: ['collection-banner', 'collection-recommendations']
43
+ * })
44
+ *
45
+ * return (
46
+ * <div>
47
+ * <h1>{collection.name} Collection</h1>
48
+ * <div id="collection-banner" />
49
+ * {/\* Product grid here *\/}
50
+ * <div id="collection-recommendations" />
51
+ * </div>
52
+ * )
53
+ * }
54
+ * ```
55
+ *
15
56
  * @group Hooks
16
57
  */
17
58
  export function useNostoCategory({ category, placements }: NostoCategoryProps) {
@@ -9,6 +9,46 @@ export type NostoCheckoutProps = { placements?: string[] }
9
9
  /**
10
10
  * You can personalise your cart and checkout pages by using the useNostoCheckout hook.
11
11
  *
12
+ * @example Basic cart page usage
13
+ * ```tsx
14
+ * import { useNostoCheckout } from '@nosto/nosto-react'
15
+ *
16
+ * function CartPage() {
17
+ * useNostoCheckout({
18
+ * placements: ['cartpage-nosto-1', 'cartpage-cross-sell']
19
+ * })
20
+ *
21
+ * return (
22
+ * <div>
23
+ * <h1>Your Cart</h1>
24
+ * {/\* Cart items here *\/}
25
+ * <div id="cartpage-cross-sell" />
26
+ * <div id="cartpage-nosto-1" />
27
+ * </div>
28
+ * )
29
+ * }
30
+ * ```
31
+ *
32
+ * @example Checkout page with recommendations
33
+ * ```tsx
34
+ * import { useNostoCheckout } from '@nosto/nosto-react'
35
+ *
36
+ * function CheckoutPage() {
37
+ * useNostoCheckout({
38
+ * placements: ['checkout-upsell', 'checkout-last-chance']
39
+ * })
40
+ *
41
+ * return (
42
+ * <div>
43
+ * <h1>Checkout</h1>
44
+ * <div id="checkout-upsell" />
45
+ * {/\* Checkout form here *\/}
46
+ * <div id="checkout-last-chance" />
47
+ * </div>
48
+ * )
49
+ * }
50
+ * ```
51
+ *
12
52
  * @group Hooks
13
53
  */
14
54
  export function useNostoCheckout(props?: NostoCheckoutProps) {
@@ -4,7 +4,45 @@ import { NostoContext, NostoContextType } from "../context"
4
4
  /**
5
5
  * A hook that allows you to access the NostoContext and retrieve Nosto-related data from it in React components.
6
6
  *
7
- * @group Essential Functions
7
+ * @example
8
+ * ```tsx
9
+ * import { useNostoContext } from '@nosto/nosto-react'
10
+ *
11
+ * function NostoStatus() {
12
+ * const { clientScriptLoaded, account, responseMode } = useNostoContext()
13
+ *
14
+ * return (
15
+ * <div>
16
+ * <p>Account: {account}</p>
17
+ * <p>Script loaded: {String(clientScriptLoaded)}</p>
18
+ * <p>Response mode: {responseMode}</p>
19
+ * </div>
20
+ * )
21
+ * }
22
+ * ```
23
+ *
24
+ * @example Using context for conditional rendering
25
+ * ```tsx
26
+ * import { useNostoContext } from '@nosto/nosto-react'
27
+ *
28
+ * function ConditionalRecommendations() {
29
+ * const { clientScriptLoaded, recommendationComponent } = useNostoContext()
30
+ *
31
+ * if (!clientScriptLoaded) {
32
+ * return <div>Loading recommendations...</div>
33
+ * }
34
+ *
35
+ * return (
36
+ * <div>
37
+ * {recommendationComponent && (
38
+ * <div id="nosto-recommendation-placeholder" />
39
+ * )}
40
+ * </div>
41
+ * )
42
+ * }
43
+ * ```
44
+ *
45
+ * @group Hooks
8
46
  */
9
47
  export function useNostoContext(): NostoContextType {
10
48
  return useContext(NostoContext)
@@ -9,6 +9,43 @@ export type NostoHomeProps = { placements?: string[] }
9
9
  /**
10
10
  * You can personalise your home page by using the useNostoHome hook.
11
11
  *
12
+ * @example Basic home page usage
13
+ * ```tsx
14
+ * import { useNostoHome } from '@nosto/nosto-react'
15
+ *
16
+ * function HomePage() {
17
+ * useNostoHome({
18
+ * placements: ['frontpage-nosto-1', 'frontpage-nosto-2', 'frontpage-hero']
19
+ * })
20
+ *
21
+ * return (
22
+ * <div>
23
+ * <div id="frontpage-hero" />
24
+ * <h1>Welcome to our store</h1>
25
+ * <div id="frontpage-nosto-1" />
26
+ * <div id="frontpage-nosto-2" />
27
+ * </div>
28
+ * )
29
+ * }
30
+ * ```
31
+ *
32
+ * @example Home page with default placements
33
+ * ```tsx
34
+ * import { useNostoHome } from '@nosto/nosto-react'
35
+ *
36
+ * function SimpleHomePage() {
37
+ * // Uses all available placements configured in Nosto admin
38
+ * useNostoHome()
39
+ *
40
+ * return (
41
+ * <div>
42
+ * <h1>Home Page</h1>
43
+ * {/\* Nosto will inject content into configured placements *\/}
44
+ * </div>
45
+ * )
46
+ * }
47
+ * ```
48
+ *
12
49
  * @group Hooks
13
50
  */
14
51
  export function useNostoHome(props?: NostoHomeProps) {
@@ -15,6 +15,57 @@ export type NostoOrderProps = {
15
15
  /**
16
16
  * You can personalise your order-confirmation/thank-you page by using the `useNostoOrder` hook.
17
17
  *
18
+ * @example Basic order confirmation page usage
19
+ * ```tsx
20
+ * import { useNostoOrder } from '@nosto/nosto-react'
21
+ *
22
+ * function OrderConfirmationPage({ order }: { order: Order }) {
23
+ * useNostoOrder({
24
+ * order: order,
25
+ * placements: ['orderconfirm-nosto-1', 'orderconfirm-cross-sell']
26
+ * })
27
+ *
28
+ * return (
29
+ * <div>
30
+ * <h1>Order Confirmed!</h1>
31
+ * <p>Order #{order.order_number}</p>
32
+ * <div id="orderconfirm-cross-sell" />
33
+ * <div id="orderconfirm-nosto-1" />
34
+ * </div>
35
+ * )
36
+ * }
37
+ * ```
38
+ *
39
+ * @example Complete order with camelCase conversion
40
+ * ```tsx
41
+ * import { useNostoOrder } from '@nosto/nosto-react'
42
+ *
43
+ * function ThankYouPage({ orderData }: { orderData: ToCamelCase<Order> }) {
44
+ * useNostoOrder({
45
+ * order: {
46
+ * orderNumber: orderData.orderNumber,
47
+ * customerEmail: orderData.customerEmail,
48
+ * purchasedItems: orderData.purchasedItems.map(item => ({
49
+ * productId: item.productId,
50
+ * skuId: item.skuId,
51
+ * name: item.name,
52
+ * unitPrice: item.unitPrice,
53
+ * quantity: item.quantity
54
+ * }))
55
+ * },
56
+ * placements: ['thankyou-recommendations', 'thankyou-reviews']
57
+ * })
58
+ *
59
+ * return (
60
+ * <div>
61
+ * <h1>Thank you for your purchase!</h1>
62
+ * <div id="thankyou-recommendations" />
63
+ * <div id="thankyou-reviews" />
64
+ * </div>
65
+ * )
66
+ * }
67
+ * ```
68
+ *
18
69
  * @group Hooks
19
70
  */
20
71
  export function useNostoOrder({ order, placements }: NostoOrderProps) {
@@ -9,6 +9,64 @@ export type NostoOtherProps = { placements?: string[] }
9
9
  /**
10
10
  * You can personalise your miscellaneous pages by using the useNostoOther hook.
11
11
  *
12
+ * @example Basic usage for contact page
13
+ * ```tsx
14
+ * import { useNostoOther } from '@nosto/nosto-react'
15
+ *
16
+ * function ContactPage() {
17
+ * useNostoOther({
18
+ * placements: ['contactpage-nosto-1', 'contactpage-popular-products']
19
+ * })
20
+ *
21
+ * return (
22
+ * <div>
23
+ * <h1>Contact Us</h1>
24
+ * <form>
25
+ * {/\* Contact form fields *\/}
26
+ * </form>
27
+ * <div id="contactpage-popular-products" />
28
+ * <div id="contactpage-nosto-1" />
29
+ * </div>
30
+ * )
31
+ * }
32
+ * ```
33
+ *
34
+ * @example About page with recommendations
35
+ * ```tsx
36
+ * import { useNostoOther } from '@nosto/nosto-react'
37
+ *
38
+ * function AboutPage() {
39
+ * useNostoOther({
40
+ * placements: ['aboutpage-featured', 'aboutpage-bestsellers']
41
+ * })
42
+ *
43
+ * return (
44
+ * <div>
45
+ * <h1>About Our Company</h1>
46
+ * <p>Learn more about our story...</p>
47
+ * <div id="aboutpage-featured" />
48
+ * <div id="aboutpage-bestsellers" />
49
+ * </div>
50
+ * )
51
+ * }
52
+ * ```
53
+ *
54
+ * @example Default placements for any other page
55
+ * ```tsx
56
+ * import { useNostoOther } from '@nosto/nosto-react'
57
+ *
58
+ * function GenericPage() {
59
+ * useNostoOther() // Uses all available placements
60
+ *
61
+ * return (
62
+ * <div>
63
+ * <h1>Generic Page</h1>
64
+ * <p>This could be any page type not covered by other hooks.</p>
65
+ * </div>
66
+ * )
67
+ * }
68
+ * ```
69
+ *
12
70
  * @group Hooks
13
71
  */
14
72
  export function useNostoOther(props?: NostoOtherProps) {
@@ -15,6 +15,58 @@ export type NostoProductProps = {
15
15
  /**
16
16
  * You can personalise your product pages by using the useNostoProduct hook.
17
17
  *
18
+ * @example Basic product page usage
19
+ * ```tsx
20
+ * import { useNostoProduct } from '@nosto/nosto-react'
21
+ *
22
+ * function ProductPage({ productId }: { productId: string }) {
23
+ * useNostoProduct({
24
+ * product: productId,
25
+ * placements: ['productpage-nosto-1', 'productpage-nosto-2']
26
+ * })
27
+ *
28
+ * return (
29
+ * <div>
30
+ * <h1>Product Details</h1>
31
+ * <div id="productpage-nosto-1" />
32
+ * <div id="productpage-nosto-2" />
33
+ * </div>
34
+ * )
35
+ * }
36
+ * ```
37
+ *
38
+ * @example Advanced product page with full tagging
39
+ * ```tsx
40
+ * import { useNostoProduct } from '@nosto/nosto-react'
41
+ *
42
+ * function AdvancedProductPage({ product }: { product: Product }) {
43
+ * useNostoProduct({
44
+ * product: product.id,
45
+ * reference: `product-${product.id}`,
46
+ * tagging: {
47
+ * product_id: product.id,
48
+ * name: product.name,
49
+ * url: product.url,
50
+ * price: product.price,
51
+ * list_price: product.listPrice,
52
+ * image_url: product.imageUrl,
53
+ * categories: product.categories,
54
+ * brand: product.brand,
55
+ * selected_sku_id: product.selectedVariant?.id
56
+ * },
57
+ * placements: ['productpage-cross-sell', 'productpage-upsell']
58
+ * })
59
+ *
60
+ * return (
61
+ * <div>
62
+ * <h1>{product.name}</h1>
63
+ * <div id="productpage-cross-sell" />
64
+ * <div id="productpage-upsell" />
65
+ * </div>
66
+ * )
67
+ * }
68
+ * ```
69
+ *
18
70
  * @group Hooks
19
71
  */
20
72
  export function useNostoProduct({ product, tagging, placements, reference }: NostoProductProps) {
@@ -12,6 +12,55 @@ export type NostoSearchProps = {
12
12
  /**
13
13
  * You can personalise your search pages by using the useNostoSearch hook.
14
14
  *
15
+ * @example Basic search page usage
16
+ * ```tsx
17
+ * import { useNostoSearch } from '@nosto/nosto-react'
18
+ *
19
+ * function SearchPage({ searchQuery }: { searchQuery: string }) {
20
+ * useNostoSearch({
21
+ * query: searchQuery,
22
+ * placements: ['searchpage-nosto-1', 'searchpage-no-results']
23
+ * })
24
+ *
25
+ * return (
26
+ * <div>
27
+ * <h1>Search Results for "{searchQuery}"</h1>
28
+ * {/\* Search results here *\/}
29
+ * <div id="searchpage-nosto-1" />
30
+ * <div id="searchpage-no-results" />
31
+ * </div>
32
+ * )
33
+ * }
34
+ * ```
35
+ *
36
+ * @example Search with dynamic query updates
37
+ * ```tsx
38
+ * import { useNostoSearch } from '@nosto/nosto-react'
39
+ * import { useState } from 'react'
40
+ *
41
+ * function SearchPageWithFilters() {
42
+ * const [query, setQuery] = useState('')
43
+ *
44
+ * useNostoSearch({
45
+ * query: query,
46
+ * placements: ['search-recommendations', 'search-trending']
47
+ * })
48
+ *
49
+ * return (
50
+ * <div>
51
+ * <input
52
+ * type="text"
53
+ * value={query}
54
+ * onChange={(e) => setQuery(e.target.value)}
55
+ * placeholder="Search products..."
56
+ * />
57
+ * <div id="search-recommendations" />
58
+ * <div id="search-trending" />
59
+ * </div>
60
+ * )
61
+ * }
62
+ * ```
63
+ *
15
64
  * @group Hooks
16
65
  */
17
66
  export function useNostoSearch({ query, placements }: NostoSearchProps) {
@@ -16,6 +16,62 @@ export type NostoSessionProps = {
16
16
  /**
17
17
  * Nosto React requires that you pass it the details of current cart contents and the details of the currently logged-in customer, if any, on every route change.
18
18
  *
19
+ * @example Basic session management
20
+ * ```tsx
21
+ * import { useNostoSession } from '@nosto/nosto-react'
22
+ *
23
+ * function App({ cart, user }: { cart?: Cart, user?: Customer }) {
24
+ * useNostoSession({
25
+ * cart: cart ? {
26
+ * items: cart.items.map(item => ({
27
+ * product_id: item.productId,
28
+ * sku_id: item.skuId,
29
+ * name: item.name,
30
+ * unit_price: item.price,
31
+ * quantity: item.quantity
32
+ * }))
33
+ * } : undefined,
34
+ * customer: user ? {
35
+ * customer_reference: user.id,
36
+ * email: user.email,
37
+ * first_name: user.firstName,
38
+ * last_name: user.lastName
39
+ * } : undefined
40
+ * })
41
+ *
42
+ * return <div>App content</div>
43
+ * }
44
+ * ```
45
+ *
46
+ * @example Route-based session updates
47
+ * ```tsx
48
+ * import { useNostoSession } from '@nosto/nosto-react'
49
+ * import { useEffect, useState } from 'react'
50
+ *
51
+ * function SessionProvider({ children }: { children: React.ReactNode }) {
52
+ * const [cart, setCart] = useState<Cart | null>(null)
53
+ * const [customer, setCustomer] = useState<Customer | null>(null)
54
+ *
55
+ * useEffect(() => {
56
+ * // Load cart and customer data
57
+ * loadCartData().then(setCart)
58
+ * loadCustomerData().then(setCustomer)
59
+ * }, [])
60
+ *
61
+ * useNostoSession({
62
+ * cart: cart ? {
63
+ * items: cart.items
64
+ * } : undefined,
65
+ * customer: customer ? {
66
+ * customerReference: customer.id,
67
+ * email: customer.email
68
+ * } : undefined
69
+ * })
70
+ *
71
+ * return <>{children}</>
72
+ * }
73
+ * ```
74
+ *
19
75
  * @group Hooks
20
76
  */
21
77
  export function useNostoSession({ cart, customer }: NostoSessionProps = {}) {
@@ -14,7 +14,6 @@ function RecommendationComponentWrapper(props: {
14
14
  nostoRecommendation: Recommendation
15
15
  }) {
16
16
  return cloneElement(props.recommendationComponent, {
17
- // eslint-disable-next-line react/prop-types
18
17
  nostoRecommendation: props.nostoRecommendation
19
18
  })
20
19
  }
@@ -31,6 +30,66 @@ function injectCampaigns(data: CampaignData) {
31
30
  injectPlacements(data.recommendations)
32
31
  }
33
32
 
33
+ /**
34
+ * Hook for rendering Nosto campaigns and recommendations. Handles both HTML and client-side rendering modes.
35
+ * This hook is typically used internally by other Nosto hooks but can be used directly for custom implementations.
36
+ *
37
+ * @example Basic usage with HTML mode
38
+ * ```tsx
39
+ * import { useRenderCampaigns } from '@nosto/nosto-react'
40
+ *
41
+ * function CustomRecommendations() {
42
+ * const { renderCampaigns } = useRenderCampaigns()
43
+ *
44
+ * useEffect(() => {
45
+ * // When using HTML response mode, campaigns are injected as HTML
46
+ * const campaignData = {
47
+ * campaigns: {
48
+ * content: {
49
+ * 'my-placement-id': '<div>Nosto content</div>'
50
+ * }
51
+ * },
52
+ * recommendations: {}
53
+ * }
54
+ * renderCampaigns(campaignData)
55
+ * }, [renderCampaigns])
56
+ *
57
+ * return <div id="my-placement-id" />
58
+ * }
59
+ * ```
60
+ *
61
+ * @example Client-side rendering with React components
62
+ * ```tsx
63
+ * import { useRenderCampaigns } from '@nosto/nosto-react'
64
+ *
65
+ * function ClientSideRecommendations() {
66
+ * const { renderCampaigns } = useRenderCampaigns()
67
+ *
68
+ * useEffect(() => {
69
+ * // When using client-side rendering, recommendations are rendered as React components
70
+ * const campaignData = {
71
+ * campaigns: {
72
+ * recommendations: {
73
+ * 'my-placement-id': {
74
+ * result_id: 'abc123',
75
+ * products: [
76
+ * { product_id: '1', name: 'Product 1', url: '/product/1' },
77
+ * { product_id: '2', name: 'Product 2', url: '/product/2' }
78
+ * ]
79
+ * }
80
+ * }
81
+ * },
82
+ * recommendations: {}
83
+ * }
84
+ * renderCampaigns(campaignData)
85
+ * }, [renderCampaigns])
86
+ *
87
+ * return <div id="my-placement-id" />
88
+ * }
89
+ * ```
90
+ *
91
+ * @group Hooks
92
+ */
34
93
  export function useRenderCampaigns() {
35
94
  const { responseMode, recommendationComponent } = useNostoContext()
36
95
  const placementRefs = useRef<Record<string, Root>>({})