Skip to content

Conversation

@tomrf1
Copy link
Member

@tomrf1 tomrf1 commented Feb 9, 2026

Background

The Feast thrasher is displayed on fronts to advertise the Feast app.
Currently it is implemented as an interactive atom in the interactives repo: https://github.com/guardian/interactives/tree/main/thrashers/feast-ny-thrasher

Screenshot 2026-02-09 at 09 57 10

It is added in the Fronts tool as a "snap link" using the CAPI url:
Screenshot 2026-02-09 at 09 57 55

The flow for updating it is:

  1. Build (html/css/js) locally and publish to S3
  2. CAPI picks this up as an atom
  3. On web, the html/js is inserted into the front container using dangerouslySetInnerHTML
  4. On apps, MAPI returns the url of a page with the interactive atom and it is rendered in a web view inserted into the Front

Problems with this approach:

  1. Bad Developer Experience compared with working on dotcom-rendering. E.g. for the web we cannot reuse dynamic/interactive components from Source/DCAR, like the Ticker.
  2. Security concerns about inserting arbitrary js into fronts

We'd prefer to build + maintain thrasher components in dotcom-rendering, for use by web and apps.

This change

Adds a new component, FeastThrasher. Includes a storybook entry.
The component looks broadly the same, though it now aligns better with the front layout.

This is served as a stand alone page from a new DCR endpoint, /AppsComponent/thrasher/feast. This is a very minimal page for use by the apps so that they can render it in a webview, just as we currently do for the interactive atoms. It includes necessary fonts, meaning we no longer need the font-face definitions.
We'll need changes to Frontend and Router to expose this endpoint.

For now nothing will actually use this component.
Future PRs will:

  1. change dotcom-rendering to use the FeastThrasher component directly from the FrontLayout, instead of using an interactive atom.
  2. change MAPI to return the new url (https://api.nextgen.guardianapps.co.uk/component/thrasher/feast?dcr=apps) for use by the apps. It's likely there will be some styling fixes needed for apps compatibility, but we need to integration test to get to that point.

Later we should also consider how to improve how this is configured in the Fronts tool. But to begin with we can continue using the old snap link as an indication that the FeastThrasher should be used.

Testing

I tested how it looks in a front by temporarily adding code to the FrontLayout (see commit) -

Desktop:
Screenshot 2026-02-09 at 13 39 58

Tablet:
Screenshot 2026-02-09 at 15 00 18

Mobile:
Screenshot 2026-02-09 at 13 40 13

@tomrf1 tomrf1 added the feature Departmental tracking: work on a new feature label Feb 9, 2026
@github-actions
Copy link

github-actions bot commented Feb 9, 2026

@tomrf1 tomrf1 force-pushed the tf-feast-component branch from 8c1bab9 to 749bc8b Compare February 9, 2026 13:57
@tomrf1 tomrf1 added the run_chromatic Runs chromatic when label is applied label Feb 9, 2026
@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Feb 9, 2026
@tomrf1 tomrf1 added the run_chromatic Runs chromatic when label is applied label Feb 9, 2026
@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Feb 9, 2026
app.post('/AppsBlocks', handleAppsBlocks);
app.post('/EditionsCrossword', handleEditionsCrossword);
app.post('/AppsHostedContent', handleAppsHostedContent);
app.post('/AppsComponent/Thrasher/:name', handleAppsThrasher);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could just be a GET endpoint, not sure how much it matters

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to POST a payload in future, if/when we make the copy and image configurable

@tomrf1 tomrf1 force-pushed the tf-feast-component branch from d10cf47 to 6111162 Compare February 10, 2026 14:00
@tomrf1 tomrf1 added the run_chromatic Runs chromatic when label is applied label Feb 10, 2026
@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Feb 10, 2026
@tomrf1 tomrf1 added the run_chromatic Runs chromatic when label is applied label Feb 10, 2026
@github-actions github-actions bot removed the run_chromatic Runs chromatic when label is applied label Feb 10, 2026
Copy link
Contributor

@JamieB-gu JamieB-gu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! A few comments and questions.

Comment on lines +2 to +12
import { FeastThrasher } from './FeastThrasher';

const meta = {
title: 'Components/Marketing/Thrashers/FeastThrasher',
component: FeastThrasher,
} satisfies Meta<typeof FeastThrasher>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given there's only one story, if you do this it hoists the story in the sidebar1:

Suggested change
import { FeastThrasher } from './FeastThrasher';
const meta = {
title: 'Components/Marketing/Thrashers/FeastThrasher',
component: FeastThrasher,
} satisfies Meta<typeof FeastThrasher>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {};
import { FeastThrasher as FeastThrasherComponent } from './FeastThrasher';
const meta = {
title: 'Components/Marketing/Thrashers/FeastThrasher',
component: FeastThrasherComponent,
} satisfies Meta<typeof FeastThrasherComponent>;
export default meta;
type Story = StoryObj<typeof meta>;
export const FeastThrasher: Story = {};

Footnotes

  1. https://storybook.js.org/docs/writing-stories/naming-components-and-hierarchy#single-story-hoisting


export const renderThrasher = (name: ThrasherName): { html: string } => {
const { html, extractedCss } = renderToStringWithEmotion(
<div>{name === 'feast' && <FeastThrasher />}</div>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for checking that the name is "feast" if the type already guarantees this? Also, do you need to wrap this in a div? In other words, could this just be?

Suggested change
<div>{name === 'feast' && <FeastThrasher />}</div>,
<FeastThrasher />,

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is redundant - but because this handler is intended to support more thrashers in future (with the ThrasherName type) it felt right to include the check anyway. I'd be ok with simplifying for now though

SvgGuardianLogo,
} from '@guardian/source/react-components';

const styles = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, where does this pattern of creating a single object to store styles come from?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's quite common in the marketing components, e.g. banners. I guess the alternative is adding a Styles suffix to various names

Comment on lines +108 to +111
const tabletImageUrl =
'https://media.guim.co.uk/2c4014d476310737dce1e830186e5b6fe18d3327/0_0_2000_2000/2000.jpg';
const mobileAndDesktopImageUrl =
'https://media.guim.co.uk/a0cc02db1394f8710bdce008e2297759098d53b3/0_0_2000_1200/2000.png';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to use the image optimiser to make these images more responsive to different screen sizes and pixel densities? Given this isn't live yet, this could be done in a follow-up change.

/**
* Generates a URL for calling the Fastly Image Optimiser.
*
* @see https://developer.fastly.com/reference/io/
* @see https://github.com/guardian/fastly-image-service/blob/main/fastly-io_guim_co_uk/src/main/resources/varnish/main.vcl
*
*/
export const generateImageURL = ({

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These urls are copied over from the existing thrasher in the interactives repo. I'll take a look at the optimiser

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess one of the benefits of moving it into dotcom-rendering is that we get to reuse features like this!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Departmental tracking: work on a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants