Skip to main content

Feeds

The StoryList component allows you to display a feed of stories on your website. Use this module to integrate interactive stories into your user interface.

Quick start​

Story Feed

import { StoryList, StoryManagerConfig, IASContainer, storyManager } from "@inappstory/react-sdk"

export const App = () => {
const storyManagerConfig: StoryManagerConfig = {
apiKey: "{projectToken}",
};

<IASContainer config={storyManagerConfig}>
<StoryList feedSlug="default" />
</IASContainer>
}

Customization​

StoriesList options​

VariableTypeDescription
cardobjectSlider card item options
layoutobjectSlider layout options
sidePaddingnumberSlider side padding, px. Default 20
topPaddingnumberSlider top padding, px. Default 20
bottomPaddingnumberSlider bottom padding, px. Default 20
bottomMarginnumberSlider bottom margin, px. Default 17
navigationobjectSlider navigation options
directionstringSlider direction. Can be horizontal or vertical (for vertical slider). Default horizontal.
autoScrollOnStoryClosebooleanAuto scroll to invisible card when closing the story reader, px. Default true

Layout​

layout property controls the slider.

VariableTypeDescription
heightnumber | nullSlider total height, px. 0 - for auto height. Default 0
backgroundColorstringDefault transparent
sliderAlignstringHorizontal align slider inside widget, variants: start, center and end. Default start.

Preview card​

Using card property you can fully customize the look of your preview cards.

VariableTypeDescription
titleobjectSee below
title.displaybooleanDetermines whether to show the title or not
title.positionstringTitle position.
Variants: cardInsideTop (inside card, at top), cardInsideBottom (inside card, at bottom), cardOutsideTop (above the card) and cardOutsideBottom(under the card). Default - cardInsideBottom
Note when using the cardOutsideTop or cardOutsideBottom option you must manually specify a slider layout height value.
title.textAlignstringText in title horizontal align, variants: start, center and end. Default start.
title.lineClampnumberNumbers of text lines. Default 4.
title.paddingnumber | stringNumber, px eq for all sides.
String - valid css, for customizing each side. Default 10.6
title.fontstringCSS valid font value. Override font.
Default normal 1rem InternalPrimaryFont where InternalPrimaryFont - primary font, loaded in project settings.
gapnumberSpace between cards, px. Default 10
heightnumberCard height, px. Default 178
variantstringCard style, one of circle, quad, rectangle. Default rectangle
borderobjectSee below
border.radiusnumberCard border radius, px. Default 21
border.colorstringCard border color, valid css. Default black
border.widthnumberCard border width, px. Default 2
border.gapnumberSpace between card and border, px. Default 5
border.gradientstringCard gradient border. Default null
boxShadowstring | nullCard box-shadow, valid css value. Default null
dropShadowstring | nullCard drop-shadow, valid css value. Example - 1px 2px 8px rgba(34, 34, 34, 0.3). Default null.
opacitynumberCard opacity. Default null
maskobject | nullCard mask - overlay between card image and title. Default null
svgMaskobject | nullOptions for card overlay with svg
openedobject | nullContain keys: border, boxShadow, opacity, mask
Apply this values (if current value not null) on card in opened state. Default all values null
coverQualitystringQuality for stories list covers, variants medium, high. If not set - sdk uses high image quality.
Default high.
coverLazyLoadingbooleanDetermines whether to use lazy loading of stories list covers.
Default false.

Preview card gradient border​

You have the ability to set a gradient border for your preview cards.

ParameterTypeDefaultDescription
card.border.gradientstring?transparentBorder background, CSS valid gradient or color
card.border.widthnumber2Card gradient border width, px
card.opened.border.gradientstring?card.border.gradientBorder background, CSS valid gradient or color in opened card state. The default value is the value set in card.border.gradient
card.opened.border.widthnumber2Card gradient border width in opened card state, px
<StoryList options={{
card: {
border: {
gradient: "linear-gradient(37deg, red, blue)",
width: 3
},
opened: {
border: {
gradient: "red",
width: 3
}
}
}
}} />

Preview card mask​

An overlay between card image and title. Can be a solid color:

<StoryList options={{
card: {
mask: {
color: "rgba(0,0,0,.3)";
}
}
}} />

Or a linear gradient

<StoryList options={{
card: {
mask: {
linearGradient: [
{
direction: "to bottom",
points: [
"rgba(0, 0, 0, 0) 48.74%",
"rgba(255, 0, 0, 0.6) 75.3%",
"rgba(255, 0, 0, 0.6) 100%",
],
},
];
}
}
}} />

Preview card SVG mask​

You can use SVG masks to create custom card overlay effects, like in the example below:

Card SVG Mask Example


Options

The svgMask property allows you to configure card and overlay masks.

PropertyTypeDescription
cardMaskstring?The base card SVG mask. Default: null.
overlayMaskArray<OverlayMaskItem>An array of overlay masks applied on top of the card. Default: null.

OverlayMaskItem

Each overlay mask item supports the following fields:

PropertyTypeDescription
maskstring?The SVG mask source. Default: null.
backgroundstring?A CSS color or any CSS background option. Default: null.

Important Notes ⚠️

  • The SVG mask must match the size and shape of the card.
  • The transparent part of the mask cuts out the content beneath it.
  • Always include the following attributes in the <svg> tag:
    width="100%" height="auto" xmlns="http://www.w3.org/2000/svg"

Example

<StoryList options={{
card: {
svgMask: {
cardMask: `<svg width="100%" height="auto" viewBox="0 0 140 140" xmlns="http://www.w3.org/2000/svg">
<path d="M70 140C108.66 140 140 108.66 140 70C140 56.1 135.95 43.14 128.96 32.25C126.22 35.17 122.32 37 118 37C109.72 37 103 30.28 103 22C103 17.68 104.83 13.78 107.76 11.04C96.86 4.05 83.9 0 70 0C31.34 0 0 31.34 0 70C0 108.66 31.34 140 70 140Z" fill="#B6B6B6"/>
</svg>`,
overlayMask: [
{
mask: `<svg width="100%" height="auto" viewBox="0 0 140 140" xmlns="http://www.w3.org/2000/svg">
<path d="M128 22C128 27.52 123.52 32 118 32C112.48 32 108 27.52 108 22C108 16.48 112.48 12 118 12C123.52 12 128 16.48 128 22Z" fill="#B6B6B6"/>
</svg>`,
background: "#F2473D",
},
],
},
}
}} />

Card MaskOverlay Mask
Card MaskOverlay Mask

By default, controls are displayed as round buttons with arrow icons on the edges of the slider. Use the navigation property to control it.

VariableTypeDescription
showControlsbooleanEnable slider controls. Default false
controlsSizenumberButton size, px. Default 48
controlsBackgroundColorstringCSS valid color value. Default #ffffff
controlsColorstringCSS valid color value. Default #000000

Custom loader​

enum LoadStatus {
loading = "Loading",
success = "LoadSuccess",
fail = "LoadFail"
}

export const App = () => {
const [loadStatus, setLoadStatus] = useState<LoadStatus>(LoadStatus.loading);

const onLoadStart = () => {
console.log("LOADING START");
setLoadStatus(LoadStatus.loading);
};

const onLoadEnd = (loadStatus: StoryListLoadStatus) => {
if (loadStatus.defaultListLength > 0 || loadStatus.favoriteListLength > 0) {
setLoadStatus(LoadStatus.success);
} else {
setLoadStatus(LoadStatus.fail);
}

if (loadStatus.error != null) {
console.error({
name: loadStatus.error.name,
networkStatus: loadStatus.error.networkStatus,
networkMessage: loadStatus.error.networkMessage
});
} else {
console.log("LOADING SUCCESS", loadStatus);
}
};

return <IASContainer config={{}}>
<div style={{
opacity: 0,
height: 0,
transition: `opacity 0.5s ease-in-out`,
...(loadStatus === LoadStatus.success ? {
height: "auto",
opacity: 1
} : {})
}}
>
<StoryList feedSlug="default" onLoadStart={onLoadStart} onLoadEnd={onLoadEnd} />
</div>
</IASContainer>
}

Reload stories​

You can use the story list API to reload the feed. For example, after changing tags:

import { StoryList, IASContainer, StoryListApi } from "@inappstory/react-sdk";

export const App = () => {
const [tags, setTags] = useState<string[]>([]);
const storyListRef = useRef<StoryListApi | null>(null);

useEffect(() => {
storyListRef.current?.reload() // Reload stories after tags changed
}, [tags])

useEffect(() => {
setTags(['new-tag'])
}, [])

return <IASContainer config={{ apiKey: "{projectToken}", tags }}>
<StoryList ref={storyListRef} feed="default" />
</IASContainer>

Custom story card​

You can customize the appearance of the story card

<StoryList
options={storyListOptions}
feedSlug="default"
storyCardView={(storyCard) => {
return <div>{storyCard.title}</div>
}}
/>

StoryCard properties

FieldTypeDescription
idnumberUnique identifier of the story
titlestringTitle of the story
titleColorstringColor of the title in HEX format
backgroundColorstringBackground color of the card in HEX format
background{ color: string; gradient: boolean }Background configuration of the card
favoritebooleanIndicates if the story is marked as favorite
likenumberNumber of likes the story has received
deeplinkstring?Deeplink URL for navigation when the card is clicked
hideInReaderboolean?Whether the story should be hidden in the reader
gameInstance{ id?: string } | undefinedDetails of the game instance, if the story is a game
hasAudioboolean?Indicates if the story contains audio
isFavoriteCardbooleanWhether this card represents a favorite stories collection
isUgcCardbooleanWhether this is a User Generated Content (UGC) card
isOpenedbooleanWhether the story is currently opened/being viewed
videoCoverSrcstring?URL of the video cover (if available)

StoryCard methods

  • getAspectRatio(variant: StoriesListCardViewVariant): number
    Calculates the aspect ratio of the card based on the specified view variant.

  • getImageSrcByCoverQuality(coverQuality: StoriesListCardCoverQuality): string | undefined
    Returns the image URL in the requested quality or undefined if not available.

  • onClick(index?: number): void
    Handles click interaction with the story card, optionally with the card's position index.

Custom favorite card​

You can customize the appearance of the favorite card

<StoryList
options={storyListOptions}
feedSlug="default"
favoriteCardView={({ favoriteStories }) => {
return (
<div>
{favoriteStories.map(favoriteStory => {
const imageSrc = favoriteStory.getImageSrcByCoverQuality("medium");
const height = 100;
return (
<div key={favoriteStory.id}>
{favoriteStory.title}
<img width={favoriteStory.getAspectRatio("rectangle") * height} height={height} src={imageSrc} />
</div>
);
})}
</div>
);
}}
/>

Custom story list controls​

You can customize story feed controls

<StoryList
options={storyListOptions}
feedSlug="default"
navigationView={(navigation) => {
return <div>
<button onClick={navigation.onPrev}>Назад</button>
<button onClick={navigation.onNext}>Π’ΠΏΠ΅Ρ€Π΅Π΄</button>
</div>
}}
/>

You can also use default styles:

<StoryList
options={storyListOptions}
feedSlug="default"
navigationView={(navigation) => {
return <div className={navigation.defaultStyles["stories-list-controls"] + " " + navigation.defaultStyles[navigation.slider.direction]}>
<button className={navigation.defaultStyles["control-start"] + " " + navigation.reachStart} onClick={navigation.onPrev}>Назад</button>
<button className={navigation.defaultStyles["control-end"] + " " + navigation.reachEnd} onClick={navigation.onNext}>Π’ΠΏΠ΅Ρ€Π΅Π΄</button>
</div>
}}
/>

Note: You can determine whether the beginning or end of the story feed has reached using the flags reachStart and reachEnd respectively.

The full interface of the passed navigation object is shown below:

VariableTypeDescription
defaultStylesobjectSet of default CSS classes
reachStartbooleanTakes true when the list reaches its beginning (initial position)
reachEndbooleanTakes true when the list reach last story card
onPrevfunctionPrev button click handler
onNextfunctionNext button click handler
slider.currentPositionnumberCurrent offset of story list
slider.direction"horizontal" | "vertical"Current story list scroll direction
slider.viewportDOMRectCurrent story list viewport

Grid view​

The <StoryList> supports a Grid View mode, providing users with a visually engaging way to browse their favorite stories. The Grid View is responsive and adapts to different screen sizes and orientations, ensuring a consistent experience across devices such as desktops, tablets, and smartphones.

To enable the Grid View display mode you need to use the direction property from stories list options. See the example below.

import { StoryList, IASContainer, storyManager } from "@inappstory/react-sdk"

export const App = () => {
return (
<IASContainer config={{ apiKey: "{projectToken}" }}>
<StoryList feedSlug="default" options={{
direction: "vertical",
layout: { height: 300 /* Height of story list */ }
}} />
</IASContainer>
}

Making Responsive​

This guide demonstrates how to create a responsive story list using the @inappstory/react-sdk package by leveraging the useMediaQuery hook. This approach allows you to adjust the appearance of story cards based on the screen size, enhancing the user experience across different devices.

Implementing useMediaQuery​

First, let's create the useMediaQuery hook that will be used to detect screen sizes:

import { useState, useEffect } from 'react';

export const useMediaQuery = (query: string) => {
const [matches, setMatches] = useState(false);

useEffect(() => {
const mediaQueryList = window.matchMedia(query);
const updateMatches = (event: MediaQueryListEvent) => {
setMatches(event.matches);
};
setMatches(mediaQueryList.matches);
mediaQueryList.addEventListener('change', updateMatches);
return () => {
mediaQueryList.removeEventListener('change', updateMatches);
};
}, [query]);

return matches;
}

It's important to note that this is just one way to implement useMediaQuery. There are various other approaches and libraries that offer similar functionality, such as:

  • react-responsive: A popular library that provides hooks and components to manage media queries in React.
  • useMedia: A hook from the usehooks-ts library that offers a simple and intuitive way to work with media queries.
  • Custom Implementations: You can also create your own version of useMediaQuery tailored to specific requirements, potentially adding features like caching, SSR support, or more complex query management.

Feel free to explore these alternatives and choose the one that best fits your project's needs.

Usage Example​

Now, we can use the useMediaQuery hook to make the story list responsive:

import React from "react";
import { StoryList, IASContainer } from "@inappstory/react-sdk";
import { useMediaQuery } from './useMediaQuery'; // Import the useMediaQuery hook

export const App = () => {
// Determine if the device is a desktop based on screen width
const isDesktop = useMediaQuery('(min-width: 1024px)');

// Set story list options based on screen size
const storyListOptions = isDesktop ? {
card: { height: 178 }
} : {
card: { height: 100 }
};

return (
<IASContainer config={{
apiKey: "{projectToken}" // Replace with your project token
}}>
<StoryList
options={storyListOptions}
feedSlug="default"
/>
</IASContainer>
);
};

Explanation​

  • useMediaQuery: This custom hook is used to detect the screen size. In this example, it checks if the screen width is at least 1024 pixels (min-width: 1024px), which indicates a desktop device.

  • storyListOptions: Depending on whether the device is a desktop or a mobile, the height of the story cards is adjusted. For desktop screens, the card height is set to 178px, while for smaller screens (like mobile devices), it is set to 100px.

Why Use useMediaQuery?​

Using the useMediaQuery hook provides a straightforward way to create responsive UIs that adapt to different screen sizes. This ensures that your content is optimally displayed on both desktop and mobile devices, enhancing the user experience.

Events​

import { storyManager } from "@inappstory/react-sdk"

storyManager.on("feedImpression", (payload) => console.log(payload));

feedImpression​

Description: Triggered after stories appear in the viewport.

Payload:

FieldTypeDescription
feedstringFeed name.
storiesarrayArray of stories { id: number; title: string; slidesCount: number }.

visibleAreaUpdated​

Description: Triggered when the visible area of stories is updated in the viewport.

Payload:

FieldTypeDescription
feedstringFeed name.
storiesarrayArray of stories { id: number; title: string; slidesCount: number }.

clickOnStory​

Description: Triggered when a user clicks on a story card in the slider list.

Payload:

FieldTypeDescription
idnumberStory ID.
titlestring?Story title.
slidesCountnumber?Number of slides in the story.
feedstring?Feed name where the story belongs.
sourcestring?Story source.
filterobjectApplied filters.
ugcPayloadobjectUGC-related payload.
defaultListLengthnumberDefault list length.
favoriteListLengthnumberFavorite list length.
indexnumberIndex of the story in the list.
isDeeplinkbooleanWhether the story was opened via deeplink.
urlstring?Deeplink URL.