diff --git a/src/SliceZone.tsx b/src/SliceZone.tsx
index b68c242..286046b 100644
--- a/src/SliceZone.tsx
+++ b/src/SliceZone.tsx
@@ -1,6 +1,19 @@
 import * as React from "react";
 import * as prismic from "@prismicio/client";
 
+import { pascalCase, PascalCase } from "./lib/pascalCase";
+
+/**
+ * Returns the type of a `SliceLike` type.
+ *
+ * @typeParam Slice - The Slice from which the type will be extracted.
+ */
+type ExtractSliceType<Slice extends SliceLike> = Slice extends SliceLikeRestV2
+	? Slice["slice_type"]
+	: Slice extends SliceLikeGraphQL
+	? Slice["type"]
+	: never;
+
 /**
  * The minimum required properties to represent a Prismic Slice from the Prismic
  * Rest API V2 for the `<SliceZone>` component.
@@ -101,6 +114,37 @@ export type SliceComponentType<
 	TContext = unknown,
 > = React.ComponentType<SliceComponentProps<TSlice, TContext>>;
 
+/**
+ * A record of Slice types mapped to a React component. The component will be
+ * rendered for each instance of its Slice.
+ *
+ * @deprecated This type is no longer used by `@prismicio/react`. Prefer using
+ *   `Record<string, SliceComponentType<any>>` instead.
+ * @typeParam TSlice - The type(s) of a Slice in the Slice Zone.
+ * @typeParam TContext - Arbitrary data made available to all Slice components.
+ */
+export type SliceZoneComponents<
+	TSlice extends SliceLike = SliceLike,
+	TContext = unknown,
+> =
+	// This is purposely not wrapped in Partial to ensure a component is provided
+	// for all Slice types. <SliceZone> will render a default component if one is
+	// not provided, but it *should* be a type error if an explicit component is
+	// missing.
+	//
+	// If a developer purposely does not want to provide a component, they can
+	// assign it to the TODOSliceComponent exported from this package. This
+	// signals to future developers that it is a placeholder and should be
+	// implemented.
+	{
+		[SliceType in ExtractSliceType<TSlice>]: SliceComponentType<
+			Extract<TSlice, SliceLike<SliceType>> extends never
+				? SliceLike
+				: Extract<TSlice, SliceLike<SliceType>>,
+			TContext
+		>;
+	};
+
 /**
  * This Slice component can be used as a reminder to provide a proper
  * implementation.
@@ -130,6 +174,54 @@ export const TODOSliceComponent = <TSlice extends SliceLike, TContext>({
 	}
 };
 
+/**
+ * Arguments for a `<SliceZone>` `resolver` function.
+ */
+type SliceZoneResolverArgs<
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	TSlice extends SliceLike = any,
+> = {
+	/**
+	 * The Slice to resolve to a React component.
+	 */
+	slice: TSlice;
+
+	/**
+	 * The name of the Slice.
+	 */
+	sliceName: PascalCase<ExtractSliceType<TSlice>>;
+
+	/**
+	 * The index of the Slice in the Slice Zone.
+	 */
+	i: number;
+};
+
+/**
+ * A function that determines the rendered React component for each Slice in the
+ * Slice Zone. If a nullish value is returned, the component will fallback to
+ * the `components` or `defaultComponent` props to determine the rendered
+ * component.
+ *
+ * @deprecated Use the `components` prop instead.
+ *
+ * @param args - Arguments for the resolver function.
+ *
+ * @returns The React component to render for a Slice.
+ */
+export type SliceZoneResolver<
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	TSlice extends SliceLike = any,
+	TContext = unknown,
+> = (args: SliceZoneResolverArgs<TSlice>) =>
+	| SliceComponentType<
+			// eslint-disable-next-line @typescript-eslint/no-explicit-any
+			any,
+			TContext
+	  >
+	| undefined
+	| null;
+
 /**
  * React props for the `<SliceZone>` component.
  *
@@ -148,6 +240,19 @@ export type SliceZoneProps<TContext = unknown> = {
 	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 	components?: Record<string, SliceComponentType<any, TContext>>;
 
+	/**
+	 * A function that determines the rendered React component for each Slice in
+	 * the Slice Zone.
+	 *
+	 * @deprecated Use the `components` prop instead.
+	 *
+	 * @param args - Arguments for the resolver function.
+	 *
+	 * @returns The React component to render for a Slice.
+	 */
+	// eslint-disable-next-line @typescript-eslint/no-explicit-any
+	resolver?: SliceZoneResolver<any, TContext>;
+
 	/**
 	 * The React component rendered if a component mapping from the `components`
 	 * prop cannot be found.
@@ -179,6 +284,7 @@ export type SliceZoneProps<TContext = unknown> = {
 export const SliceZone = <TContext,>({
 	slices = [],
 	components = {},
+	resolver,
 	defaultComponent = TODOSliceComponent,
 	context = {} as TContext,
 }: SliceZoneProps<TContext>): JSX.Element => {
@@ -186,9 +292,22 @@ export const SliceZone = <TContext,>({
 		return slices.map((slice, index) => {
 			const type = "slice_type" in slice ? slice.slice_type : slice.type;
 
-			const Comp =
+			let Comp =
 				components[type as keyof typeof components] || defaultComponent;
 
+			// TODO: Remove `resolver` in v3 in favor of `components`.
+			if (resolver) {
+				const resolvedComp = resolver({
+					slice,
+					sliceName: pascalCase(type),
+					i: index,
+				});
+
+				if (resolvedComp) {
+					Comp = resolvedComp as typeof Comp;
+				}
+			}
+
 			const key =
 				"id" in slice && slice.id
 					? slice.id
@@ -204,7 +323,7 @@ export const SliceZone = <TContext,>({
 				/>
 			);
 		});
-	}, [components, context, defaultComponent, slices]);
+	}, [components, context, defaultComponent, slices, resolver]);
 
 	return <>{renderedSlices}</>;
 };
diff --git a/src/index.ts b/src/index.ts
index ff60f32..ec965a4 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -28,9 +28,10 @@ export { SliceZone, TODOSliceComponent } from "./SliceZone";
 export type {
 	SliceComponentProps,
 	SliceComponentType,
-	SliceLikeRestV2,
-	SliceLikeGraphQL,
 	SliceLike,
+	SliceLikeGraphQL,
+	SliceLikeRestV2,
+	SliceZoneComponents,
 	SliceZoneLike,
 	SliceZoneProps,
 } from "./SliceZone";
diff --git a/test/SliceZone.test.tsx b/test/SliceZone.test.tsx
index 143e56c..75b9986 100644
--- a/test/SliceZone.test.tsx
+++ b/test/SliceZone.test.tsx
@@ -5,6 +5,7 @@ import { it, expect, vi } from "vitest";
 import { renderJSON } from "./__testutils__/renderJSON";
 
 import { SliceZone, TODOSliceComponent, SliceComponentProps } from "../src";
+import { SliceZoneResolver } from "../src/SliceZone";
 
 type StringifySliceComponentProps = {
 	/**
@@ -201,6 +202,67 @@ it("TODO component renders null in production", () => {
 	process.env.NODE_ENV = originalNodeEnv;
 });
 
+it("renders components from a resolver function for backwards compatibility with next-slicezone", async () => {
+	const slices = [
+		{
+			slice_type: "foo_bar",
+		},
+		{
+			slice_type: "barFoo",
+		},
+		{
+			slice_type: "baz-qux",
+		},
+	] as const;
+
+	const resolver: SliceZoneResolver<(typeof slices)[number]> = ({
+		sliceName,
+	}) => {
+		switch (sliceName) {
+			case "FooBar": {
+				return (props) => <StringifySliceComponent id="foo_bar" {...props} />;
+			}
+
+			case "BarFoo": {
+				return (props) => <StringifySliceComponent id="barFoo" {...props} />;
+			}
+
+			case "BazQux": {
+				return (props) => <StringifySliceComponent id="baz-qux" {...props} />;
+			}
+		}
+	};
+
+	const actual = renderJSON(<SliceZone slices={slices} resolver={resolver} />);
+	const expected = renderJSON(
+		<>
+			<StringifySliceComponent
+				id="foo_bar"
+				slice={slices[0]}
+				index={0}
+				slices={slices}
+				context={{}}
+			/>
+			<StringifySliceComponent
+				id="barFoo"
+				slice={slices[1]}
+				index={1}
+				slices={slices}
+				context={{}}
+			/>
+			<StringifySliceComponent
+				id="baz-qux"
+				slice={slices[2]}
+				index={2}
+				slices={slices}
+				context={{}}
+			/>
+		</>,
+	);
+
+	expect(actual).toStrictEqual(expected);
+});
+
 it("supports the GraphQL API", () => {
 	const slices = [{ type: "foo" }, { type: "bar" }] as const;