sanity-plugin-seofields 1.2.5 → 1.2.7

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 (50) hide show
  1. package/README.md +465 -0
  2. package/dist/index.cjs +2604 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +422 -0
  5. package/dist/index.d.ts +339 -492
  6. package/dist/index.js +1284 -2013
  7. package/dist/index.js.map +1 -1
  8. package/dist/next.cjs +182 -0
  9. package/dist/next.cjs.map +1 -0
  10. package/dist/next.d.cts +241 -0
  11. package/dist/next.d.ts +202 -295
  12. package/dist/next.js +110 -70
  13. package/dist/next.js.map +1 -1
  14. package/dist/types-B91ena4g.d.cts +89 -0
  15. package/dist/types-B91ena4g.d.ts +89 -0
  16. package/package.json +46 -20
  17. package/dist/index.d.mts +0 -575
  18. package/dist/index.mjs +0 -3292
  19. package/dist/index.mjs.map +0 -1
  20. package/dist/next.d.mts +0 -334
  21. package/dist/next.mjs +0 -102
  22. package/dist/next.mjs.map +0 -1
  23. package/sanity.json +0 -8
  24. package/src/components/SeoHealthDashboard.tsx +0 -1568
  25. package/src/components/SeoHealthPane.tsx +0 -81
  26. package/src/components/SeoHealthTool.tsx +0 -11
  27. package/src/components/SeoPreview.tsx +0 -178
  28. package/src/components/meta/MetaDescription.tsx +0 -39
  29. package/src/components/meta/MetaTitle.tsx +0 -44
  30. package/src/components/openGraph/OgDescription.tsx +0 -46
  31. package/src/components/openGraph/OgTitle.tsx +0 -45
  32. package/src/components/twitter/twitterDescription.tsx +0 -45
  33. package/src/components/twitter/twitterTitle.tsx +0 -45
  34. package/src/helpers/SeoMetaTags.tsx +0 -154
  35. package/src/helpers/seoMeta.ts +0 -283
  36. package/src/index.ts +0 -26
  37. package/src/next.ts +0 -12
  38. package/src/plugin.ts +0 -344
  39. package/src/schemas/index.ts +0 -121
  40. package/src/schemas/types/index.ts +0 -20
  41. package/src/schemas/types/metaAttribute/index.ts +0 -60
  42. package/src/schemas/types/metaTag/index.ts +0 -17
  43. package/src/schemas/types/openGraph/index.ts +0 -114
  44. package/src/schemas/types/robots/index.ts +0 -26
  45. package/src/schemas/types/twitter/index.ts +0 -108
  46. package/src/types.ts +0 -108
  47. package/src/utils/fieldsUtils.ts +0 -160
  48. package/src/utils/seoUtils.ts +0 -423
  49. package/src/utils/utils.ts +0 -9
  50. package/v2-incompatible.js +0 -11
package/README.md CHANGED
@@ -658,6 +658,205 @@ export function SEO({seo}) {
658
658
  }
659
659
  ```
660
660
 
661
+ ## 🎯 Framework Integration Examples
662
+
663
+ ### Remix (Loader + Action Approach)
664
+
665
+ Handle SEO metadata in Remix loaders for server-side rendering with JSON responses:
666
+
667
+ ```typescript
668
+ // routes/posts.$slug.tsx
669
+ import {json, type LoaderFunction} from '@remix-run/node'
670
+ import {useLoaderData} from '@remix-run/react'
671
+ import {buildSeoMeta} from 'sanity-plugin-seofields/utils'
672
+
673
+ export const loader: LoaderFunction = async ({params}) => {
674
+ // Fetch post with SEO fields from Sanity
675
+ const post = await sanityClient.fetch(
676
+ `*[_type == "post" && slug.current == $slug][0]{
677
+ title, content, seo, slug
678
+ }`,
679
+ {slug: params.slug},
680
+ )
681
+
682
+ // Use buildSeoMeta to generate meta tags
683
+ const seoMeta = buildSeoMeta(post.seo, {
684
+ defaultTitle: 'Blog',
685
+ siteUrl: 'https://example.com',
686
+ })
687
+
688
+ return json({post, seoMeta})
689
+ }
690
+
691
+ export const meta: MetaFunction<typeof loader> = ({data}) => {
692
+ return data?.seoMeta || []
693
+ }
694
+
695
+ export default function PostRoute() {
696
+ const {post} = useLoaderData<typeof loader>()
697
+ return <article>{post.title}</article>
698
+ }
699
+ ```
700
+
701
+ ### Nuxt 3 (Composable Approach)
702
+
703
+ Create a composable for SSR-friendly SEO management:
704
+
705
+ ```typescript
706
+ // composables/useSanityMeta.ts
707
+ import {buildSeoMeta} from 'sanity-plugin-seofields/utils'
708
+
709
+ export const useSanityMeta = (seo: SEOFields, options = {}) => {
710
+ const {
711
+ defaultTitle = 'My Site',
712
+ siteUrl = 'https://example.com',
713
+ } = options
714
+
715
+ const meta = buildSeoMeta(seo, {defaultTitle, siteUrl})
716
+
717
+ // useHead() handles SSR + client-side rendering
718
+ useHead({
719
+ title: seo?.title || defaultTitle,
720
+ meta: meta.map(m => ({
721
+ name: m.name || m.property,
722
+ content: m.content,
723
+ })),
724
+ link: seo?.canonicalUrl
725
+ ? [{rel: 'canonical', href: seo.canonicalUrl}]
726
+ : [],
727
+ })
728
+ }
729
+
730
+ // pages/blog/[slug].vue
731
+ <script setup lang="ts">
732
+ const route = useRoute()
733
+ const {data: post} = await useFetch(`/api/posts/${route.params.slug}`)
734
+
735
+ useSanityMeta(post.value?.seo, {
736
+ siteUrl: 'https://example.com',
737
+ })
738
+ </script>
739
+
740
+ <template>
741
+ <article v-if="post">
742
+ <h1>{{ post.title }}</h1>
743
+ </article>
744
+ </template>
745
+ ```
746
+
747
+ ### Astro (Server-Side Rendering)
748
+
749
+ Leverage Astro's component-level SEO with static generation:
750
+
751
+ ```typescript
752
+ // src/pages/blog/[slug].astro
753
+ ---
754
+ import {buildSeoMeta} from 'sanity-plugin-seofields/utils'
755
+ import Layout from '../../layouts/Layout.astro'
756
+
757
+ // Fetch from Sanity at build time
758
+ const {slug} = Astro.params
759
+ const post = await sanityClient.fetch(
760
+ `*[_type == "post" && slug.current == $slug][0]{
761
+ title, content, seo, slug
762
+ }`,
763
+ {slug},
764
+ )
765
+
766
+ // Generate meta tags for static HTML
767
+ const seoMeta = buildSeoMeta(post.seo, {
768
+ defaultTitle: 'Blog',
769
+ siteUrl: Astro.site,
770
+ })
771
+ ---
772
+
773
+ <Layout
774
+ title={post.seo?.title}
775
+ meta={seoMeta}
776
+ canonicalUrl={post.seo?.canonicalUrl}
777
+ >
778
+ <article>
779
+ <h1>{post.title}</h1>
780
+ </article>
781
+ </Layout>
782
+
783
+ <!-- Astro layouts handle meta tag rendering -->
784
+ ```
785
+
786
+ ### React SPA (Client-Side with Helmet)
787
+
788
+ For client-rendered React apps without SSR:
789
+
790
+ ```typescript
791
+ // components/PostHead.tsx
792
+ import {Helmet} from 'react-helmet-async'
793
+ import type {SEOFields} from 'sanity-plugin-seofields'
794
+
795
+ interface PostHeadProps {
796
+ seo?: SEOFields
797
+ fallbackTitle: string
798
+ }
799
+
800
+ export function PostHead({seo, fallbackTitle}: PostHeadProps) {
801
+ return (
802
+ <Helmet>
803
+ {/* Basic Meta */}
804
+ <title>{seo?.title || fallbackTitle}</title>
805
+ <meta name="description" content={seo?.description || ''} />
806
+
807
+ {/* Open Graph - critical for social shares */}
808
+ <meta property="og:title" content={seo?.openGraph?.title} />
809
+ <meta property="og:description" content={seo?.openGraph?.description} />
810
+ {seo?.openGraph?.image?.url && (
811
+ <meta property="og:image" content={seo.openGraph.image.url} />
812
+ )}
813
+
814
+ {/* Robots */}
815
+ {seo?.robots?.noIndex && <meta name="robots" content="noindex" />}
816
+
817
+ {/* Canonical (limit crawl budget) */}
818
+ {seo?.canonicalUrl && (
819
+ <link rel="canonical" href={seo.canonicalUrl} />
820
+ )}
821
+ </Helmet>
822
+ )
823
+ }
824
+
825
+ // Usage in page component
826
+ // Note: Client-side rendering cannot inject meta tags pre-page-load.
827
+ // For public pages, use SSR or static generation instead.
828
+ ```
829
+
830
+ ---
831
+
832
+ ## 🚀 Migrating from Other SEO Plugins
833
+
834
+ Coming from **Yoast**, **All in One SEO**, or **RankMath**?
835
+
836
+ | Feature | Yoast | All in One SEO | RankMath | sanity-plugin-seofields |
837
+ |---------|-------|----------------|----------|------------------------|
838
+ | **Meta Title/Description** | ✅ | ✅ | ✅ | ✅ |
839
+ | **Open Graph Tags** | ✅ | ✅ | ✅ | ✅ |
840
+ | **Twitter Cards** | ⚠️ Limited | ✅ | ✅ | ✅ |
841
+ | **Readability Analysis** | ✅ | ✅ | ✅ | ❌ (Sanity-native focus) |
842
+ | **Keyword Density** | ✅ | ✅ | ✅ | ❌ (External tools) |
843
+ | **Custom Meta Attributes** | ⚠️ Limited | ✅ | ✅ | ✅ |
844
+ | **Robots/Canonical** | ✅ | ✅ | ✅ | ✅ |
845
+ | **Headless-First** | ❌ | ❌ | ❌ | ✅ Framework-agnostic |
846
+ | **SSR-Ready** | N/A | N/A | N/A | ✅ All frameworks |
847
+
848
+ ### Migration Path
849
+
850
+ 1. **Export existing metadata** from your old plugin (title, description, OG tags)
851
+ 2. **Create a Sanity schema** matching your current fields — map to `seoFields` type
852
+ 3. **Bulk import** using Sanity's API or migration scripts
853
+ 4. **Update your frontend** to use `buildSeoMeta` utilities instead of plugin hooks
854
+ 5. **Test meta rendering** in browsers DevTools and social preview tools
855
+
856
+ For detailed migration guides, see [Migration Guides](#) in our documentation.
857
+
858
+ ---
859
+
661
860
  ## 📚 API Reference
662
861
 
663
862
  ### Main Export
@@ -675,6 +874,272 @@ import seofields from 'sanity-plugin-seofields'
675
874
  - `metaAttribute` - Individual meta attribute
676
875
  - `robots` - Search engine robots settings
677
876
 
877
+ ## 🔧 Troubleshooting
878
+
879
+ ### TypeScript auto-import not working
880
+
881
+ **Problem:** `buildSeoMeta` doesn't appear in IDE autocomplete
882
+
883
+ **Solution:**
884
+
885
+ 1. Check your `package.json` exports field has a `"types"` condition:
886
+ ```json
887
+ {
888
+ "exports": {
889
+ ".": {
890
+ "types": "./dist/index.d.ts",
891
+ "default": "./dist/index.js"
892
+ },
893
+ "./next": {
894
+ "types": "./dist/next.d.ts",
895
+ "default": "./dist/next.js"
896
+ }
897
+ }
898
+ }
899
+ ```
900
+
901
+ 2. Verify your `tsconfig.json` has the correct `moduleResolution`:
902
+ ```json
903
+ {
904
+ "compilerOptions": {
905
+ "moduleResolution": "bundler",
906
+ "resolveJsonModule": true
907
+ }
908
+ }
909
+ ```
910
+
911
+ ---
912
+
913
+ ### "Cannot find module 'sanity-plugin-seofields/next'"
914
+
915
+ **Problem:** Runtime import error when trying to use Next.js utilities
916
+
917
+ **Solution:**
918
+
919
+ 1. Ensure built files exist in `dist/next.js`:
920
+ ```bash
921
+ npm run build
922
+ ```
923
+
924
+ 2. Clear and reinstall node_modules:
925
+ ```bash
926
+ rm -rf node_modules package-lock.json
927
+ npm install
928
+ ```
929
+
930
+ 3. Verify `package.json` exports includes the next export:
931
+ ```json
932
+ {
933
+ "exports": {
934
+ "./next": {
935
+ "types": "./dist/next.d.ts",
936
+ "default": "./dist/next.js"
937
+ }
938
+ }
939
+ }
940
+ ```
941
+
942
+ ---
943
+
944
+ ### Type inference in generateMetadata()
945
+
946
+ **Problem:** `buildSeoMeta()` return type is not recognized as Next.js `Metadata`
947
+
948
+ **Solution:** Explicitly type the return value:
949
+
950
+ ```tsx
951
+ import type {Metadata} from 'next'
952
+ import {buildSeoMeta} from 'sanity-plugin-seofields/next'
953
+
954
+ export async function generateMetadata(): Promise<Metadata> {
955
+ const seoData = await fetchSeoData()
956
+ const metadata = buildSeoMeta(seoData)
957
+
958
+ return {
959
+ title: metadata.title,
960
+ description: metadata.description,
961
+ openGraph: {
962
+ title: metadata.openGraph?.title,
963
+ description: metadata.openGraph?.description,
964
+ url: metadata.openGraph?.url,
965
+ },
966
+ twitter: {
967
+ card: metadata.twitter?.card as any,
968
+ site: metadata.twitter?.site,
969
+ creator: metadata.twitter?.creator,
970
+ },
971
+ }
972
+ }
973
+ ```
974
+
975
+ ---
976
+
977
+ ### Dashboard not showing in Sanity Studio
978
+
979
+ **Problem:** SEO Health tool doesn't appear in the studio
980
+
981
+ **Solution:**
982
+
983
+ 1. Ensure the plugin is added to `sanity.config.ts`:
984
+ ```typescript
985
+ import seofields from 'sanity-plugin-seofields'
986
+
987
+ export default defineConfig({
988
+ // ... other config
989
+ plugins: [
990
+ seofields({
991
+ documentTypes: ['post', 'page', 'product'],
992
+ // other options
993
+ }),
994
+ ],
995
+ })
996
+ ```
997
+
998
+ 2. Check that `documentTypes` array includes your document types:
999
+ ```typescript
1000
+ seofields({
1001
+ documentTypes: ['post', 'page'], // Add your document types here
1002
+ })
1003
+ ```
1004
+
1005
+ 3. Verify plugin config fieldVisibility is not hiding SEO fields:
1006
+ ```typescript
1007
+ seofields({
1008
+ documentTypes: ['post'],
1009
+ fieldVisibility: {
1010
+ // Make sure SEO fields aren't set to hidden
1011
+ },
1012
+ })
1013
+ ```
1014
+
1015
+ ---
1016
+
1017
+ ### Image URLs not resolving
1018
+
1019
+ **Problem:** OG/Twitter images show as `undefined` in meta tags
1020
+
1021
+ **Solution:** Provide an `imageUrlResolver` function:
1022
+
1023
+ ```tsx
1024
+ import imageUrlBuilder from '@sanity/image-url'
1025
+ import {client} from './sanity.client'
1026
+
1027
+ const imageBuilder = imageUrlBuilder(client)
1028
+
1029
+ export function buildImageUrl(source) {
1030
+ if (!source) return undefined
1031
+ return imageBuilder.image(source).url()
1032
+ }
1033
+
1034
+ // In your buildSeoMeta call:
1035
+ const metadata = buildSeoMeta({
1036
+ ...seoData,
1037
+ imageUrlResolver: buildImageUrl,
1038
+ })
1039
+ ```
1040
+
1041
+ Or use it in your Next.js layout:
1042
+
1043
+ ```tsx
1044
+ import {buildSeoMeta} from 'sanity-plugin-seofields/next'
1045
+ import imageUrlBuilder from '@sanity/image-url'
1046
+
1047
+ const imageBuilder = imageUrlBuilder(client)
1048
+
1049
+ export async function generateMetadata(): Promise<Metadata> {
1050
+ const seoData = await sanityFetch(SeoQuery)
1051
+
1052
+ const metadata = buildSeoMeta({
1053
+ ...seoData,
1054
+ imageUrlResolver: (image) => imageBuilder.image(image).url(),
1055
+ })
1056
+
1057
+ return metadata
1058
+ }
1059
+ ```
1060
+
1061
+ ---
1062
+
1063
+ ### generateMetadata() not finding Sanity data
1064
+
1065
+ **Problem:** Data is `undefined` when trying to fetch from Sanity in Next.js
1066
+
1067
+ **Solution:**
1068
+
1069
+ 1. Ensure `sanityFetch` is properly awaited:
1070
+ ```tsx
1071
+ import {sanityFetch} from '@/lib/sanity.client'
1072
+
1073
+ export async function generateMetadata(): Promise<Metadata> {
1074
+ try {
1075
+ const seoData = await sanityFetch(SeoQuery) // Don't forget await!
1076
+ return buildSeoMeta(seoData)
1077
+ } catch (error) {
1078
+ console.error('Failed to fetch SEO data:', error)
1079
+ return {title: 'Default Title'}
1080
+ }
1081
+ }
1082
+ ```
1083
+
1084
+ 2. Verify environment variables are set:
1085
+ ```bash
1086
+ # .env.local
1087
+ NEXT_PUBLIC_SANITY_PROJECT_ID=your_project_id
1088
+ NEXT_PUBLIC_SANITY_DATASET=production
1089
+ SANITY_API_TOKEN=your_token (if using authenticated fetches)
1090
+ ```
1091
+
1092
+ 3. Complete example with proper error handling:
1093
+ ```tsx
1094
+ import type {Metadata} from 'next'
1095
+ import {buildSeoMeta} from 'sanity-plugin-seofields/next'
1096
+ import {sanityFetch} from '@/lib/sanity.client'
1097
+
1098
+ const SeoQuery = `*[_type == "post" && slug.current == $slug][0] {
1099
+ title,
1100
+ seo {
1101
+ title,
1102
+ description,
1103
+ openGraph {
1104
+ title,
1105
+ description,
1106
+ image,
1107
+ },
1108
+ twitter {
1109
+ card,
1110
+ site,
1111
+ creator,
1112
+ },
1113
+ },
1114
+ }`
1115
+
1116
+ export async function generateMetadata({
1117
+ params,
1118
+ }: {
1119
+ params: {slug: string}
1120
+ }): Promise<Metadata> {
1121
+ try {
1122
+ const doc = await sanityFetch(SeoQuery, {slug: params.slug})
1123
+
1124
+ if (!doc) {
1125
+ return {title: 'Post not found'}
1126
+ }
1127
+
1128
+ return buildSeoMeta(doc.seo || {})
1129
+ } catch (error) {
1130
+ console.error('SEO metadata error:', error)
1131
+ return {title: 'Error loading page'}
1132
+ }
1133
+ }
1134
+ ```
1135
+
1136
+ ---
1137
+
1138
+ **Still stuck?** Check our:
1139
+ - 📖 [Full Documentation](./TYPES_SCHEMA_DOCS.md)
1140
+ - 🐛 [GitHub Issues](https://github.com/hardik-143/sanity-plugin-seofields/issues)
1141
+ - 📧 [Email Support](mailto:dhardik1430@gmail.com)
1142
+
678
1143
  ## 🤝 Contributing
679
1144
 
680
1145
  Contributions are welcome! Please feel free to submit a Pull Request.