Improve annotation accuracy

The Annotations API lets you deeply integrate Cord's annotation functionality into your product, creating a seamless experience.


By supplying the "glue code" between the annotation flow and your UI, you ensure annotations always match what's on the screen. It's perfect for complex features with multiple states, like charts, text editors, maps, and more.

Video player with an annotation coming from the Cord sidebar

Set the annotation's location #

The most important concept behind the Annotations API is the annotation location. A Location is a flat object you associate with a part of your document, that describes, in your app's terminology, what is being annotated. Cord stores this Location object alongside the annotation, and we surface it when it's time to render the annotation pin.

For example, in a chart component (either written entirely by you or using a library like Highcharts, Chart.js, etc), you might describe the point being annotated as:

JSON:
{
  "page": "dashboard",
  "chart": "revenue",
  "month": 3,
  "year": 2022
}
{
  "page": "dashboard",
  "chart": "revenue",
  "month": 3,
  "year": 2022
}
Copy

Or, for a video player, you might describe an annotation taken at timestamp 1:25 as:

JSON:
{
    "page": "dashboard",
    "video": "dramatic-gopher.mp4",
    "time": 85
  }
{
    "page": "dashboard",
    "video": "dramatic-gopher.mp4",
    "time": 85
  }
Copy

An annotation target is an HTML element in your application's DOM decorated with a data-cord-annotation-location attribute, with the value being a stable serialization of the location object that best represents that area of the document.

When the user starts the annotation flow from the Cord sidebar and clicks to place the annotation pin somewhere on the page, we check if the click is within an annotation target, by checking its DOM ancestors for any data-cord-annotation-location attribute.

Decorate your DOM with the annotation location #

The useCordAnnotationTargetRef hook helps you decorate HTML elements with thedata-cord-annotation-location attribute representing the location for annotations captured within that element (and its DOM subtree). This hook handles serializing the location object and attaching it to the element as the data attribute so you don't have to.

React:
import { useCordAnnotationTargetRef } from "@cord-sdk/react";

function VideoPlayerComponent({ videoSrc }) {
  const location = { page: "dashboard", video: videoSrc };
  const videoElementRef = useCordAnnotationTargetRef(location);

  return <video src={videoSrc} ref={videoElementRef} />;
}
import { useCordAnnotationTargetRef } from "@cord-sdk/react";

function VideoPlayerComponent({ videoSrc }) {
  const location = { page: "dashboard", video: videoSrc };
  const videoElementRef = useCordAnnotationTargetRef(location);

  return <video src={videoSrc} ref={videoElementRef} />;
}
Copy

With just this addition to your DOM, any time the user places an annotation on the <video> element, we store the annotation location as, for example, { page: "dashboard", video: "dramatic-gopher.mp4" }. When other users see that annotation, it will point to an element in the page that matches that location.

Define dynamic locations (Optional) #

If your annotations need to include extra location information that is highly dynamic, dependent on where exactly the user clicked, or otherwise can't be expressed at render time, you can provide that through theuseCordAnnotationCaptureHandler hook.

For example, you might want to remember the timestamp the video annotation was taken at, so that you can skip the video back to that exact timestamp when the user clicks the annotation in Cord.

React with TypeScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
React with JavaScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

function VideoPlayerComponent({ videoSrc }) {
  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
Copy

Add location-specific labels (Optional) #

The useCordAnnotationCaptureHandler hook can also be used to define a location-specific label for the annotation -- user-visible text that describes what is being annotated -- by returning a label field. If you don't provide one, we default to "Annotation".

React with TypeScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
React with JavaScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

function VideoPlayerComponent({ videoSrc }) {
  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
Copy

Control where the annotation pin is rendered (Optional) #

You can control the exact annotation pin position on the page through the useCordAnnotationRenderer hook. The function you provide will be called any time the pin positions on the page are refreshed. The function receives theAnnotation object as an argument. You can return either absolute document coordinates, or coordinates relative to a DOM element.

React with TypeScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  // The callback provided to this hook will receive the location
  // object associated with the annotation. This is the same
  // object that we constructed in the above code where we
  // combined the `location` object with the `extraLocation`
  // field in the `useCordAnnotationCaptureHandler` hook.
  useCordAnnotationRenderer(location, (annotationLocation: VideoPlayerAnnotation) => {
    if (!videoElementRef.current) {
      // if the video element is for some reason not rendered yet,
      // don't show the annotation pin
      return;
    }

    // If the video player's current time isn't close to the
    // timestamp where the annotation was created, don't show
    // the annotation.
    if (
      annotationLocation.time < videoElementRef.current.currentTime - 2.5 &&
      annotationLocation.time > videoElementRef.current.currentTime + 2.5
    ) {
      return;
    }

    // If we made it here, we know that the annotation makes sense to show, so
    // we'll put in on the video player in the bottom right corner.
    return {
      coordinates: { x: "90%", y: "90%" },
      element: videoElementRef.current,
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
React with JavaScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

function VideoPlayerComponent({ videoSrc }) {
  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  // The callback provided to this hook will receive the location
  // object associated with the annotation. This is the same
  // object that we constructed in the above code where we
  // combined the `location` object with the `extraLocation`
  // field in the `useCordAnnotationCaptureHandler` hook.
  useCordAnnotationRenderer(location, (annotationLocation) => {
    if (!videoElementRef.current) {
      // if the video element is for some reason not rendered yet,
      // don't show the annotation pin
      return;
    }

    // If the video player's current time isn't close to the
    // timestamp where the annotation was created, don't show
    // the annotation.
    if (
      annotationLocation.time < videoElementRef.current.currentTime - 2.5 &&
      annotationLocation.time > videoElementRef.current.currentTime + 2.5
    ) {
      return;
    }

    // If we made it here, we know that the annotation makes sense to show, so
    // we'll put in on the video player in the bottom right corner.
    return {
      coordinates: { x: "90%", y: "90%" },
      element: videoElementRef.current,
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  // The callback provided to this hook will receive the location
  // object associated with the annotation. This is the same
  // object that we constructed in the above code where we
  // combined the `location` object with the `extraLocation`
  // field in the `useCordAnnotationCaptureHandler` hook.
  useCordAnnotationRenderer(location, (annotationLocation: VideoPlayerAnnotation) => {
    if (!videoElementRef.current) {
      // if the video element is for some reason not rendered yet,
      // don't show the annotation pin
      return;
    }

    // If the video player's current time isn't close to the
    // timestamp where the annotation was created, don't show
    // the annotation.
    if (
      annotationLocation.time < videoElementRef.current.currentTime - 2.5 &&
      annotationLocation.time > videoElementRef.current.currentTime + 2.5
    ) {
      return;
    }

    // If we made it here, we know that the annotation makes sense to show, so
    // we'll put in on the video player in the bottom right corner.
    return {
      coordinates: { x: "90%", y: "90%" },
      element: videoElementRef.current,
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
Copy

Only allow annotations on some parts of your page (Optional) #

By default, the entire page can be annotated, which means the user can leave annotations on parts of the page that are irrelevant, like side navigation, top header, etc. You can control which parts of your page the user can place annotations on using thedata-cord-annotation-allowed attribute.

By adding data-cord-annotation-allowed=false on an element you can disable the annotation feature on that element and its entire DOM subtree.

React:
<body>
  <div id="sidebar" data-cord-annotation-allowed="false">
    {/* Cord annotations are not allowed here */
  </div>
  <div id="content">
    {/*
        Cord annotations are allowed here because
        they are allowed by default
    */}
  </div>
</body>
HTML:
<body>
  <div id="sidebar" data-cord-annotation-allowed="false">
    <!-- cord annotations are not allowed here -->
  </div>
  <div id="content">
    <!-- cord annotations are allowed here (by default) -->
  </div>
</body>
<body>
  <div id="sidebar" data-cord-annotation-allowed="false">
    {/* Cord annotations are not allowed here */
  </div>
  <div id="content">
    {/*
        Cord annotations are allowed here because
        they are allowed by default
    */}
  </div>
</body>
Copy

Another use case is to disallow annotations on the entire document except for specific elements. You can do that by adding data-cord-annotation-allowed=false on a top element like <body> and then selectively enabling it on specific elements, with data-cord-annotation-allowed=true. In this case, you might want to screenshot only these elements. To do so, you can use the ScreenshotConfig API.

React:
<body data-cord-annotation-allowed="false">
  {/* The line above disables annotations everywhere */}

  <div id="sidebar" />
  <div id="content" data-cord-annotation-allowed="true">
    {/*
       Cord annotations are allowed only on content
       within this <div> because it has the
       `data-cord-annotation-allowed` attribute
       explicitly set.
    */}
  </div>
</body>
HTML:
<!--
  Cord annotations are not allowed on <body>
  or any of its children.
-->
<body data-cord-annotation-allowed="false">
  <div id="sidebar" />
  <div id="content" data-cord-annotation-allowed="true">
    <!--
       Cord annotations are allowed only on content
       within this <div> because it has the
       `data-cord-annotation-allowed` attribute
       explicitly set.
    -->
  </div>
</body>
<body data-cord-annotation-allowed="false">
  {/* The line above disables annotations everywhere */}

  <div id="sidebar" />
  <div id="content" data-cord-annotation-allowed="true">
    {/*
       Cord annotations are allowed only on content
       within this <div> because it has the
       `data-cord-annotation-allowed` attribute
       explicitly set.
    */}
  </div>
</body>
Copy

Respond in your application when an annotation is clicked (Optional) #

When the user clicks an annotation in the sidebar or in a thread, you might need to change your app's state to show that annotation properly. For example, you may need to skip a video to the annotated timestamp, or fetch data for a chart within a specific date interval.

Through the useCordAnnotationClickHandler hook you can provide a callback that will be called with theAnnotationWithThreadID object when the user clicks on the annotation pill in the message. You're not expected to return anything from the callback; it's just an event.

React with TypeScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  // The callback provided to this hook will receive the location
  // object associated with the annotation. This is the same
  // object that we constructed in the above code where we
  // combined the `location` object with the `extraLocation`
  // field in the `useCordAnnotationCaptureHandler` hook.
  useCordAnnotationRenderer(location, (annotationLocation: VideoPlayerAnnotation) => {
    if (!videoElementRef.current) {
      // if the video element is for some reason not rendered yet,
      // don't show the annotation pin
      return;
    }

    // If the video player's current time isn't close to the
    // timestamp where the annotation was created, don't show
    // the annotation.
    if (
      annotationLocation.time < videoElementRef.current.currentTime - 2.5 &&
      annotationLocation.time > videoElementRef.current.currentTime + 2.5
    ) {
      return;
    }

    // This hook allows you to know when the user has clicked on a particular
    // annotation. You might use this to jump to a particular point in a video,
    // the scroll the page to a particular position, or otherwise change around
    // the UI to highlight what the user has shown interest in.
    useCordAnnotationClickHandler<VideoPlayerAnnotation>(
      location,
      (annotation) => {
        if (videoElementRef.current) {
          // Skip the video to the annotation timestamp and pause
          videoElementRef.current.currentTime = annotation.location.time;
          videoElementRef.current.pause();
        }
      }
    );

    // If we made it here, we know that the annotation makes sense to show, so
    // we'll put in on the video player in the bottom right corner.
    return {
      coordinates: { x: "90%", y: "90%" },
      element: videoElementRef.current,
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
React with JavaScript:
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

function VideoPlayerComponent({ videoSrc }) {
  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  // The callback provided to this hook will receive the location
  // object associated with the annotation. This is the same
  // object that we constructed in the above code where we
  // combined the `location` object with the `extraLocation`
  // field in the `useCordAnnotationCaptureHandler` hook.
  useCordAnnotationRenderer(location, (annotationLocation) => {
    if (!videoElementRef.current) {
      // if the video element is for some reason not rendered yet,
      // don't show the annotation pin
      return;
    }

    // If the video player's current time isn't close to the
    // timestamp where the annotation was created, don't show
    // the annotation.
    if (
      annotationLocation.time < videoElementRef.current.currentTime - 2.5 &&
      annotationLocation.time > videoElementRef.current.currentTime + 2.5
    ) {
      return;
    }

    // If we made it here, we know that the annotation makes sense to show, so
    // we'll put in on the video player in the bottom right corner.
    return {
      coordinates: { x: "90%", y: "90%" },
      element: videoElementRef.current,
    };
  });

    // This hook allows you to know when the user has clicked on a particular
    // annotation. You might use this to jump to a particular point in a video,
    // the scroll the page to a particular position, or otherwise change around
    // the UI to highlight what the user has shown interest in.
    useCordAnnotationClickHandler(
      location,
      (annotationWithThreadID) => {
        if (videoElementRef.current) {
          // Skip the video to the annotation timestamp and pause
          videoElementRef.current.currentTime = annotation.location.time;
          videoElementRef.current.pause();
        }
      }
    );

  return <video ref={videoElementRef} src={videoSrc} />;
}
import {
  useCordAnnotationTargetRef,
  useCordAnnotationCaptureHandler,
} from "@cord-sdk/react";

type VideoPlayerAnnotation = {
  page: string;
  video: string;
  time: number;
};

function VideoPlayerComponent({ videoSrc }) {

  // The initial location information for any annotations
  // users create.
  const location = { page: "dashboard", video: videoSrc };

  // A ref to apply to your video element. Cord will
  // use this ref when users create annotations, applying
  // the `location` object to the annotation.
  const videoElementRef = useCordAnnotationTargetRef(location);

  // The callback provided here will be used to enrich the
  // `location` data for the annotation. You would use
  // this hook when you want to put more specific location
  // information in the annotation. Here, for instance,
  // we're adding the current time of the video player
  // as extra `location` data.
  useCordAnnotationCaptureHandler<VideoPlayerAnnotation>(location, () => {
    return {
      extraLocation: {
        time: videoElementRef.current?.currentTime ?? 0,
      },

      // The `label` property is a special field. Setting
      // this value will change how the annotation is displayed
      // within Cord. Instead of showing the word "Annotation"
      // in Cord, the label provided here will be used. You
      // can use this to make annotation easier for your users
      // to understand and navigate.
      label: "Video: " + timestampToString(time),
    };
  });

  // The callback provided to this hook will receive the location
  // object associated with the annotation. This is the same
  // object that we constructed in the above code where we
  // combined the `location` object with the `extraLocation`
  // field in the `useCordAnnotationCaptureHandler` hook.
  useCordAnnotationRenderer(location, (annotationLocation: VideoPlayerAnnotation) => {
    if (!videoElementRef.current) {
      // if the video element is for some reason not rendered yet,
      // don't show the annotation pin
      return;
    }

    // If the video player's current time isn't close to the
    // timestamp where the annotation was created, don't show
    // the annotation.
    if (
      annotationLocation.time < videoElementRef.current.currentTime - 2.5 &&
      annotationLocation.time > videoElementRef.current.currentTime + 2.5
    ) {
      return;
    }

    // This hook allows you to know when the user has clicked on a particular
    // annotation. You might use this to jump to a particular point in a video,
    // the scroll the page to a particular position, or otherwise change around
    // the UI to highlight what the user has shown interest in.
    useCordAnnotationClickHandler<VideoPlayerAnnotation>(
      location,
      (annotation) => {
        if (videoElementRef.current) {
          // Skip the video to the annotation timestamp and pause
          videoElementRef.current.currentTime = annotation.location.time;
          videoElementRef.current.pause();
        }
      }
    );

    // If we made it here, we know that the annotation makes sense to show, so
    // we'll put in on the video player in the bottom right corner.
    return {
      coordinates: { x: "90%", y: "90%" },
      element: videoElementRef.current,
    };
  });

  return <video ref={videoElementRef} src={videoSrc} />;
}
Copy

Ready! #

Your app now supports rich, precise annotations.


Ask Cordy