diff --git a/README.md b/README.md
index d7ad75b..c0931be 100755
--- a/README.md
+++ b/README.md
@@ -23,84 +23,85 @@ npm i @phntms/react-share
Example usage in Next.js:
```JSX
-import { MetaHeadEmbed, TwitterHeadEmbed } from "@phntms/react-share";
-import type { AppProps } from "next/app";
+import Head from 'next/head';
+import { MetaHeadEmbed } from "@phntms/react-share";
-const App = ({ Component }: AppProps) => (
+const PageLayout: React.FC = ({children}) => {
<>
-
-
-
-
-
+ {meta}}
+ siteTitle="PHANTOM"
+ pageTitle="Our Work"
+ titleTemplate="[siteTitle] | [pageTitle]"
+ description="Transforming challenges of all shapes and sizes into inventive, engaging and performance driven solutions that change the game."
+ baseSiteUrl="https://phantom.land"
+ pagePath="work"
+ keywords={["creative-agency", "phantom", "work"]}
+ imageUrl="https://bit.ly/3wiUOuk"
+ imageAlt="PHANTOM logo."
+ twitter={{
+ cardSize: "large",
+ siteUsername: "@phntmLDN",
+ creatorUsername: "@phntmLDN",
+ }}
+ />
+ {children}
>
);
-export default App;
+export default PageLayout;
```
### <MetaHeadEmbed />
-| Property | Type | Required | Notes |
-| ----------------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| **pageTitle** | string | **Yes** | Every page should have a unique title that describes the page, such as 'Home', 'About' etc. |
-| **siteTitle** | string | **Yes** | Title of the site, usually the organization / brand name. |
-| **titleTemplate** | string | **No** | Title template used to display `pageTitle` and `siteTitle` in a template, displays the values using corresponding `[pageTitle]` and `[siteTitle]`. Example template: "[pageTitle] | [siteTitle]". |
-| **description** | string | **Yes** | A one to two sentence description of your webpage. Keep it within 160 characters, and write it to catch the user's attention. |
-| **siteBaseUrl** | string | **Yes** | Base URL of the site, excluding trailing slash. |
-| **pagePath** | string | **No** | The path of the current page, excluding leading slash. |
-| **canonicalUrl** | string | **No** | The canonical URL, if your page is a duplicate. |
-| **keywords** | string|string[] | **Yes** | List of SEO keywords describing what your webpage does. For example, `"your, tags"` or `["your", "tags"]`. |
-| **imageUrl** | string | **Yes** | Image url of asset to share. Recommended aspect ratio for landscape is 1.9:1 (1200x630) or for squares 1:1 (1200x1200). For more info, visit [here](https://iamturns.com/open-graph-image-size/). |
-| **imageAlt** | string | **Yes** | Image alt for users who are visually impaired. |
-| **locale** | string | **No** | The locale these tags are marked up in, such as; `en_GB`, `fr_FR` and `es_ES`. Defaults to `en_US`. |
+| Property | Type | Required | Notes |
+| ----------------- | -------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **render** | React.ReactNode | **Yes** | Unfortunately `react-helmet` and `next/head` are strict with how they accept meta tags. `react-helmet` doesn't support nesting. Whereas Next.JS only supports some children and not all, therefore a render function is required. |
+| **pageTitle** | string | **Yes** | Every page should have a unique title that describes the page, such as 'Home', 'About' etc. |
+| **siteTitle** | string | **Yes** | Title of the site, usually the organization / brand name. |
+| **titleTemplate** | string | **No** | Title template used to display `pageTitle` and `siteTitle` in a template, displays the values using corresponding `[pageTitle]` and `[siteTitle]`. Example template: "[pageTitle] | [siteTitle]". |
+| **description** | string | **Yes** | A one to two sentence description of your webpage. Keep it within 160 characters, and write it to catch the user's attention. |
+| **baseSiteUrl** | string | **Yes** | Base site URL, excluding trailing slash. |
+| **pagePath** | string | **No** | The path of the current page, excluding leading slash. |
+| **canonicalUrl** | string | **No** | The canonical URL, if your page is a duplicate. |
+| **keywords** | string|string[] | **No** | List of SEO keywords describing what your webpage does. Example, `"your, tags"` or `["your", "tags"]`. |
+| **imageUrl** | string | **Yes** | Image url of asset to share. Recommended aspect ratio for landscape is 1.9:1 (1200x630) or for squares 1:1 (1200x1200). For more info, visit [here](https://iamturns.com/open-graph-image-size/). |
+| **imageAlt** | string | **Yes** | Image alt for users who are visually impaired. |
+| **locale** | string | **No** | The locale these tags are marked up in, such as; `en_GB`, `fr_FR` and `es_ES`. Defaults to `en_US`. |
+| **twitter** | TwitterEmbedProps | **No** | Optional twitter embed properties to include. |
-To add all page meta properties, add `MetaHeadEmbed` to the `head` of the page.
+To use simply add `MetaHeadEmbed` to a shared layout to get the best out of page specific properties such as `pagePath`.
-**Note**: `imageUrl` and `canonicalUrl` must start with `https://`, else they won't work.
+**Note**: `baseSiteUrl` and `imageUrl` must start with `https://`, else they won't work when sharing.
-### <TwitterHeadEmbed />
+### TwitterEmbedProps
-| Property | Type | Required | Notes |
-| ------------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
-| **useLargeCard** | boolean | **No** | Summary card type. If `true`, uses large card, and if `false` uses small card. Defaults to `false`. |
-| **title** | string | **Yes** | A concise title for the related content. |
-| **description** | string | **No** | A description that concisely summarizes the content as appropriate for presentation within a Tweet. Should not be the same as title. |
-| **siteUsername** | string | **No** | The Twitter @username the card should be attributed to. |
-| **creatorUsername** | string | **No** | The Twitter @username for the content creator / author. |
-| **imageUrl** | string | **No** | Image to show in card. Images must be less than 5MB in size. Supported file types; JPG, PNG, WEBP and GIF. |
-| **imageAlt** | string | **No** | Image alt for users who are visually impaired. Maximum 420 characters. |
+| Property | Type | Required | Notes |
+| ------------------- | -------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| **cardSize** | 'small'|'large' | **Yes** | Summary card size. |
+| **title** | string | **No** | A concise title for the related content. If left blank, page title will be inherited instead. |
+| **description** | string | **No** | A description that concisely summarizes the content as appropriate for presentation within a Tweet. Should not be the same as title. If left blank, `MetaHeadEmbed` description will be inherited instead. |
+| **siteUsername** | string | **No** | The Twitter @username the card should be attributed to. |
+| **creatorUsername** | string | **No** | The Twitter @username for the content creator / author. |
+| **imageUrl** | string | **No** | Image to show in card. Images must be less than 5MB in size. Supported file types; JPG, PNG, WEBP and GIF. |
+| **imageAlt** | string | **No** | Image alt for users who are visually impaired. Maximum 420 characters. |
-`TwitterHeadEmbed` _should_ be used alongside `MetaHeadEmbed` for full sharing support.
+**Note**: Image used should be different based on `cardSize`:
-**Note**: Image used should be different based on `useLargeCard`:
+- For `large` cards, use a 2:1 aspect ratio (300x157 px minium or 4096x4096 px maximum).
+- For `small` cards, use a 1:1 aspect ratio (144x144 px minium or 4096x4096 px maximum).
-- For large cards, use a 2:1 aspect ratio (300x157 px minium or 4096x4096 px maximum).
-- For small cards, use a 1:1 aspect ratio (144x144 px minium or 4096x4096 px maximum).
+**A Note on Twitter Tags**
+
+Twitter will inherit `og:title`, `og:description` and `og:image` tags by default, so unless you want unique fields, respective fields in `TwitterEmbedProps` should be left blank to avoid duplication.
### getFacebookUrl()
-| Parameter | Type | Required | Notes |
-| --------- | ------ | -------- | -------------------------------- |
-| url | string | **Yes** | URL of shared webpage. |
-| quote | string | **No** | Quote to show in Facebook card. |
-| hashtag | string | **No** | Hashtag to show in Facebook card |
+| Parameter | Type | Required | Notes |
+| --------- | ------ | -------- | --------------------------------- |
+| url | string | **Yes** | URL of shared webpage. |
+| quote | string | **No** | Quote to show in Facebook card. |
+| hashtag | string | **No** | Hashtag to show in Facebook card. |
Basic component example usage:
@@ -108,11 +109,7 @@ Basic component example usage:
import { getFacebookUrl } from "@phntms/react-share";
const ShareToFacebook = () => (
-
+
Share to Facebook
);
@@ -122,12 +119,12 @@ export default ShareToFacebook;
### getLinkedinUrl()
-| Parameter | Type | Required | Notes |
-| --------- | ------ | -------- | ------------------------------------------------------------------------ |
-| url | string | **Yes** | URL of shared webpage. |
-| title | string | **No** | Title to show in card. |
-| summary | string | **No** | Description to show in card |
-| source | string | **No** | Source of the content (for example... your website or application name). |
+| Parameter | Type | Required | Notes |
+| --------- | ------ | -------- | --------------------------------------------------------------------- |
+| url | string | **Yes** | URL of shared webpage. |
+| title | string | **No** | Title to show in card. |
+| summary | string | **No** | Description to show in card. |
+| source | string | **No** | Source of the content. For example, your website or application name. |
Basic component example usage:
@@ -145,12 +142,12 @@ export default ShareToLinkedin;
### getTwitterUrl()
-| Parameter | Type | Required | Notes |
-| --------- | ------ | -------- | -------------------------------- |
-| url | string | **Yes** | URL of shared webpage. |
-| text | string | **No** | Text to show in Twitter card. |
-| hashtags | string | **No** | Hashtags to show in Twitter card |
-| related | string | **No** | Accounts to recommend following. |
+| Parameter | Type | Required | Notes |
+| --------- | -------------------- | -------- | ------------------------------------------------------------------------------- |
+| url | string | **Yes** | URL of shared webpage. |
+| text | string | **No** | Text to show in Twitter card. |
+| hashtags | string|string[] | **No** | Hashtags to show in Twitter card. Example, `"your,tags"` or `["your", "tags"]`. |
+| related | string|string[] | **No** | Accounts to recommend following. Example, `"your, tags"` or `["your", "tags"]`. |
Basic component example usage:
diff --git a/src/components/MetaHeadEmbed.tsx b/src/components/MetaHeadEmbed.tsx
index 56e7038..adbff1a 100644
--- a/src/components/MetaHeadEmbed.tsx
+++ b/src/components/MetaHeadEmbed.tsx
@@ -2,7 +2,53 @@ import React from "react";
import isAbsoluteUrl from "is-absolute-url";
+import { commaSeparate } from "../utils";
+
+export interface TwitterEmbedProps {
+ /** Summary card size. */
+ cardSize: "small" | "large";
+
+ /** A concise title for the related content. */
+ title?: string;
+
+ /**
+ * A description that concisely summarizes the content as appropriate for
+ * presentation within a Tweet. Should not be the same as title.
+ */
+ description?: string;
+
+ /** The Twitter @username the card should be attributed to. */
+ siteUsername?: string;
+
+ /** The Twitter @username for the content creator / author. */
+ creatorUsername?: string;
+
+ /**
+ * Image to show in card. _Should_ only be used if image is different to
+ * `MetaHeadEmbed` image.
+ *
+ * Should be different based on `useLargeCard`:
+ * - For large cards, use a 2:1 aspect ratio (300x157 px minium or
+ * 4096x4096 px maximum).
+ * - For small cards, use a 1:1 aspect ratio (144x144 px minium or
+ * 4096x4096 px maximum).
+ *
+ * Images must be less than 5MB in size.
+ *
+ * Supported file types; JPG, PNG, WEBP and GIF.
+ *
+ * Note: Only the first frame of an animated GIF will be used.
+ */
+ imageUrl?: string;
+
+ /** Image alt for users who are visually impaired. Maximum 420 characters. */
+ imageAlt?: string;
+}
+
export interface MetaEmbedProps {
+ /** Returns meta properties to be rendered. */
+ render: (meta: React.ReactNode) => JSX.Element;
+
/** Unique page title that describes the page, such as `Home`, `About` etc. */
pageTitle: string;
@@ -23,17 +69,17 @@ export interface MetaEmbedProps {
/** Canonical URL of your webpage that will be used as its default app URL. */
canonicalUrl?: string;
- /** Base URL of the site, excluding trailing slash. */
- siteBaseUrl: string;
+ /** Base site URL, excluding trailing slash. */
+ baseSiteUrl: string;
/** The path of the page, excluding leading slash. */
pagePath?: string;
/**
* List of SEO keywords describing what your webpage does.
- * For example, `"your, tags"` or `["your", "tags"]`.
+ * Example: `"your, tags"` or `["your", "tags"]`.
*/
- keywords: string | string[];
+ keywords?: string | string[];
/**
* Image url of asset to share. Recommended aspect ratio for landscape is
@@ -51,24 +97,26 @@ export interface MetaEmbedProps {
* Defaults to `en_US`.
*/
locale?: string;
+
+ /** Twitter embed properties */
+ twitter?: TwitterEmbedProps;
}
const MetaHeadEmbed = ({
+ render,
pageTitle,
siteTitle,
titleTemplate,
description,
canonicalUrl,
- siteBaseUrl,
+ baseSiteUrl,
pagePath,
keywords,
imageUrl,
imageAlt,
locale = "en_US",
+ twitter,
}: MetaEmbedProps) => {
- const joinedKeywords =
- typeof keywords === "string" ? keywords : keywords?.join(", ");
-
const title = titleTemplate
? pageTitle === siteTitle
? pageTitle
@@ -81,28 +129,78 @@ const MetaHeadEmbed = ({
canonicalUrl &&
(isAbsoluteUrl(canonicalUrl)
? canonicalUrl
- : `${siteBaseUrl}/${canonicalUrl}`);
-
- const pageUrl = pagePath ? `${siteBaseUrl}/${pagePath}` : siteBaseUrl;
-
- return (
- <>
- {title}
-
-
-
- {canonicalUrl && }
-
-
-
-
-
-
-
-
-
- >
- );
+ : `${baseSiteUrl}/${canonicalUrl}`);
+
+ const pageUrl = pagePath ? `${baseSiteUrl}/${pagePath}` : baseSiteUrl;
+
+ const metaEmbed = [
+ {title},
+ ,
+ ,
+ keywords && (
+
+ ),
+ canonicalUrl && ,
+
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ,
+ ];
+
+ const twitterEmbed = ({
+ cardSize,
+ title,
+ description,
+ siteUsername,
+ creatorUsername,
+ imageUrl,
+ imageAlt,
+ }: TwitterEmbedProps) => [
+ ,
+ title && ,
+ description && (
+
+ ),
+ siteUsername && (
+
+ ),
+ creatorUsername && (
+
+ ),
+ imageUrl && (
+
+ ),
+ imageAlt && (
+
+ ),
+ ];
+
+ return render([metaEmbed, twitter && twitterEmbed({ ...twitter })]);
};
export default MetaHeadEmbed;
diff --git a/src/components/TwitterHeadEmbed.tsx b/src/components/TwitterHeadEmbed.tsx
deleted file mode 100644
index bce3370..0000000
--- a/src/components/TwitterHeadEmbed.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import React from "react";
-
-export interface TwitterEmbedProps {
- /** Summary card type. */
- useLargeCard?: boolean;
-
- /** A concise title for the related content. */
- title: string;
-
- /**
- * A description that concisely summarizes the content as appropriate for
- * presentation within a Tweet. Should not be the same as title.
- */
- description?: string;
-
- /** The Twitter @username the card should be attributed to. */
- siteUsername?: string;
-
- /** The Twitter @username for the content creator / author. */
- creatorUsername?: string;
-
- /**
- * Image to show in card. _Should_ only be used if image is different to
- * `MetaHeadEmbed` image.
- *
- * Should be different based on `useLargeCard`:
- * - For large cards, use a 2:1 aspect ratio (300x157 px minium or
- * 4096x4096 px maximum).
- * - For small cards, use a 1:1 aspect ratio (144x144 px minium or
- * 4096x4096 px maximum).
- *
- * Images must be less than 5MB in size.
- *
- * Supported file types; JPG, PNG, WEBP and GIF.
- *
- * Note: Only the first frame of an animated GIF will be used.
- */
- imageUrl?: string;
-
- /** Image alt for users who are visually impaired. Maximum 420 characters. */
- imageAlt?: string;
-}
-
-const TwitterHeadEmbed = ({
- useLargeCard = false,
- title,
- description,
- siteUsername,
- creatorUsername,
- imageUrl,
- imageAlt,
-}: TwitterEmbedProps) => (
- <>
-
-
- {description && }
- {siteUsername && }
- {creatorUsername && (
-
- )}
- {imageUrl && }
- {imageAlt && }
- >
-);
-
-export default TwitterHeadEmbed;
diff --git a/src/index.ts b/src/index.ts
index 6debba3..1631fe0 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,12 +1,8 @@
export {
- default as SharingHeadEmbed,
+ default as MetaHeadEmbed,
MetaEmbedProps,
-} from "./components/MetaHeadEmbed";
-
-export {
- default as TwitterHeadEmbed,
TwitterEmbedProps,
-} from "./components/TwitterHeadEmbed";
+} from "./components/MetaHeadEmbed";
export {
default as getLinkedinUrl,
@@ -20,4 +16,9 @@ export {
FacebookProps,
} from "./utils/getFacebookUrl";
+export {
+ default as getShareUrl,
+ AllSocialPlatformProps,
+} from "./utils/getShareUrl";
+
export { SocialPlatforms } from "./types";
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..021c24b
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,2 @@
+export const commaSeparate = (words?: string | string[]) =>
+ typeof words === "string" ? words : words?.join(",");
diff --git a/src/utils/getFacebookUrl.ts b/src/utils/getFacebookUrl.ts
index 7fe1ef5..ebe92c7 100644
--- a/src/utils/getFacebookUrl.ts
+++ b/src/utils/getFacebookUrl.ts
@@ -6,14 +6,14 @@ export interface FacebookProps extends BaseShareProps {
quote?: string;
/** Hashtag to show in Facebook card. */
- hashtag: string;
+ hashtag?: string;
}
export const getFacebookUrl = ({ url, quote, hashtag }: FacebookProps) =>
`https://www.facebook.com/sharer/sharer.php${objectToUrlParams({
u: url,
quote,
- hashtag,
+ hashtag: hashtag?.charAt(0) === "#" ? hashtag : `#${hashtag}`,
})}`;
export default getFacebookUrl;
diff --git a/src/utils/getShareUrl.ts b/src/utils/getShareUrl.ts
index 0f53bd3..f869d6f 100644
--- a/src/utils/getShareUrl.ts
+++ b/src/utils/getShareUrl.ts
@@ -3,7 +3,9 @@ import getFacebookUrl, { FacebookProps } from "./getFacebookUrl";
import getLinkedinUrl, { LinkedinProps } from "./getLinkedinUrl";
import getTwitterUrl, { TwitterProps } from "./getTwitterUrl";
-interface Props extends FacebookProps, LinkedinProps, TwitterProps {}
+export type AllSocialPlatformProps = FacebookProps &
+ LinkedinProps &
+ TwitterProps;
export const getShareUrl = (
socialPlatform: SocialPlatforms,
@@ -17,7 +19,7 @@ export const getShareUrl = (
text,
hashtags,
related,
- }: Props
+ }: AllSocialPlatformProps
) => {
switch (socialPlatform) {
case SocialPlatforms.Facebook:
diff --git a/src/utils/getTwitterUrl.ts b/src/utils/getTwitterUrl.ts
index 99e5d32..2214df5 100644
--- a/src/utils/getTwitterUrl.ts
+++ b/src/utils/getTwitterUrl.ts
@@ -1,23 +1,30 @@
import { BaseShareProps } from "../types";
+import { commaSeparate } from "../utils";
import objectToUrlParams from "./objectToUrlParams";
export interface TwitterProps extends BaseShareProps {
/** Text to show in card. */
text?: string;
- /** Hashtags to show in Twitter card. */
- hashtags?: string[];
+ /**
+ * Hashtags to show in Twitter card.
+ * Example: `"your, tags"` or `["your", "tags"]`.
+ */
+ hashtags?: string | string[];
- /** Accounts to recommend following. */
- related?: string[];
+ /**
+ * Accounts to recommend following.
+ * Example: `"your, tags"` or `["your", "tags"]`.
+ */
+ related?: string | string[];
}
export const getTwitterUrl = ({ url, text, hashtags, related }: TwitterProps) =>
`https://twitter.com/share${objectToUrlParams({
url,
text,
- hashtags: hashtags?.join(","),
- related: related?.join(","),
+ hashtags: commaSeparate(hashtags),
+ related: commaSeparate(related),
})}`;
export default getTwitterUrl;