
Research
/Security News
PolinRider Spreads Through Compromised GitHub Accounts and Packagist
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.
@entrancekit/react
Advanced tools
Turn any intro animation — a video, a Lottie file, or your own React component — into a correct, self-removing app entrance. EntranceKit owns the lifecycle; you keep owning the animation.
Turn an intro animation you already have — a video, a Lottie file, or your own React component — into a correct app entrance, without writing the lifecycle yourself.
The animation is the easy part. The flow around it is not: playing it on load without blocking the app, honoring reduced motion, falling back on slow or failed loads, exiting by the model you chose, and tearing down cleanly. EntranceKit owns that flow. You keep owning the animation.
prefers-reduced-motion is honored by default (no flash).readyWhen, bounded so it can never trap the user.showPolicy persistence with no
SSR flash — kept on a separate subpath so the core stays gate-free.npm install @entrancekit/react
# Lottie support is optional and lazy-loaded:
npm install @lottiefiles/dotlottie-react
import { EntranceVideo } from '@entrancekit/react';
export default function Root() {
return (
<>
<App /> {/* renders and stays interactive the whole time */}
<EntranceVideo
id="launch"
sources={[
{ src: '/intro.webm', type: 'video/webm' },
{ src: '/intro.mp4', type: 'video/mp4' },
]}
poster="/intro-poster.png"
fallbackAfter={2500}
/>
</>
);
}
import { EntranceController } from '@entrancekit/react';
<EntranceController id="launch">
{({ shouldAnimate, complete, skip }) => (
<MyIntro
play={shouldAnimate} // start when told
onComplete={complete} // its OWN done-signal — the preferred path
onError={skip} // never trap the user
/>
)}
</EntranceController>;
A stunning, accessible entrance in one line — skip control, reduced-motion, and correct teardown come free. Each preset is a pure consumer of the contract and a separate tree-shakeable export, so the core stays ~5KB.
import { EntrancePreset } from '@entrancekit/react/presets';
<EntrancePreset id="launch" preset="mask-reveal" logo={<Logo />} />;
Four canonical presets: fade, curtain, mask-reveal, logo-draw (also exported as the
named components Fade, Curtain, MaskReveal, LogoDraw). When you outgrow them, eject to
EntranceController — same lifecycle, full control.
Auto-playing on mount is a lie if your fonts, hero image, or data aren't ready. readyWhen
holds the entrance in preload until a real signal settles — bounded by preloadTimeout, so
a slow or never-settling signal can never trap the user.
import { EntranceVideo, fontsReady, imageDecoded, allReady } from '@entrancekit/react';
<EntranceVideo id="launch" src="/intro.webm" readyWhen={fontsReady} />;
// compose your own signals (resolve → play, reject → graceful error):
<EntranceController
id="launch"
readyWhen={() => allReady(fontsReady(), imageDecoded('/hero.avif'), fetchSession())}
preloadTimeout={6000}
>
{(api) => <Splash play={api.shouldAnimate} onComplete={api.complete} />}
</EntranceController>;
fontsReady / imageDecoded / allReady are pure, zero-dependency, client-only helpers
exported from the package root. SSR-safe by construction — readyWhen only ever runs in a
client effect.
By default an entrance is transient — it plays and leaves, and the core never touches
storage. Opt in to "show once" with a showPolicy from the separate /policy subpath. It
runs only in a client effect, so there is no SSR flash: a skipped run resolves with
'suppressed' and never reaches play.
import { EntranceVideo } from '@entrancekit/react';
import { oncePerSession, oncePerUser, oncePerVersion } from '@entrancekit/react/policy';
<EntranceVideo id="launch" src="/intro.webm" showPolicy={oncePerSession()} />;
// oncePerUser() persists across sessions; oncePerVersion('2025-01') re-shows on a bump.
showPolicy decides whether to show, never whether to block — the overlay stays
unconditionally non-blocking.
One typed event stream over the whole lifecycle, plus opt-in User Timing marks:
<EntranceController
id="launch"
onEvent={(e) => analytics.track(e.type, e)} // { type, id, reason/state/kind, at }
performanceMarks // entrancekit:<id>:<state> marks + a play→done "visible" measure
>
{/* … */}
</EntranceController>
And a dev-only overlay (separate subpath, prod-stripped) that renders the live machine, the active watchdog countdown, and the resolve reason:
import { EntranceDevtools, useEntranceDevtools } from '@entrancekit/react/devtools';
const dt = useEntranceDevtools();
<>
<EntranceVideo id="launch" src="/intro.webm" onEvent={dt.onEvent} />
<EntranceDevtools events={dt.events} />
</>;
Thin bindings for two popular animation libraries — both optional peers, lazy-imported only when used:
import { MotionEntrance } from '@entrancekit/react/motion';
import { useGsapEntrance } from '@entrancekit/react/gsap';
<MotionEntrance id="launch" from={{ opacity: 1 }} to={{ opacity: 0 }}>
<Logo />
</MotionEntrance>;
// headless GSAP: pair with useEntrance
const api = useEntrance({ id: 'launch' });
useGsapEntrance(api, (gsap) =>
gsap.timeline().to('.logo', { autoAlpha: 0, duration: 1 })
);
EntranceKit owns one canonical state machine:
idle → preload → play → resolve → done
Every way an entrance can end — the animation's own done-signal (completed), a user skip
(skipped), a timeout, an error, a reduced-motion suppression (reduced-motion), or a
showPolicy skip (suppressed) — funnels through a single resolve carrying that reason,
so teardown is deterministic on every path. Wire your animation's native end event to
complete(); fallbackAfter is only a safety net for a hung or slow load, never a cap on a
correctly-signalling animation.
| Import | What it is |
|---|---|
EntranceController | Render-prop for a bring-your-own animation. The core. |
EntranceVideo · EntranceLottie | MP4/WebM and Lottie convenience entry points. |
useEntrance | The same engine, headless, for a fully custom overlay. |
fontsReady · imageDecoded · allReady | Readiness helpers for readyWhen. |
@entrancekit/react/presets | Fade · Curtain · MaskReveal · LogoDraw · EntrancePreset. |
@entrancekit/react/policy | oncePerSession · oncePerUser · oncePerVersion. |
@entrancekit/react/motion · …/gsap | MotionEntrance · useGsapEntrance bindings. |
@entrancekit/react/devtools | EntranceDevtools · useEntranceDevtools (dev-only). |
Shared props: id (required), readyWhen, preloadTimeout, fallbackAfter (default
4000), reducedMotion ('skip' | 'play', default 'skip'), showPolicy, skipControl
(true | render-fn | false), outro (number | { durationMs, render }), container,
onEvent, performanceMarks, and onResolve / onError / onStateChange.
Persistence is opt-in and isolated:
showPolicylives on@entrancekit/react/policy, and with no policy the core never touches storage. The overlay is never a blocking gate.
The docs site and live Playground (every source, every scenario, every exit model) is at
terminalis.github.io/entrancekit (deployed by
CI from website). Run it locally:
npm install && npm run build
npm --prefix website install
npm --prefix website run dev
MIT
FAQs
Turn any intro animation — a video, a Lottie file, or your own React component — into a correct, self-removing app entrance. EntranceKit owns the lifecycle; you keep owning the animation.
The npm package @entrancekit/react receives a total of 0 weekly downloads. As such, @entrancekit/react popularity was classified as not popular.
We found that @entrancekit/react demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.

Research
/Security News
Operators behind PolinRider used a compromised GitHub account to plant malware in four development versions of a Packagist package with 700,000+ downloads.

Security News
GitHub Actions now supports cache-mode, a least-privilege control on the Actions cache aimed at the cache poisoning technique behind recent compromises.

Company News
Allow myself to introduce... myself.