diff --git a/web/src/beta/components/Icon/Icons/fileIcon.svg b/web/src/beta/components/Icon/Icons/fileIcon.svg
new file mode 100644
index 000000000..5c3d184a8
--- /dev/null
+++ b/web/src/beta/components/Icon/Icons/fileIcon.svg
@@ -0,0 +1,4 @@
+
diff --git a/web/src/beta/components/Icon/icons.ts b/web/src/beta/components/Icon/icons.ts
new file mode 100644
index 000000000..17e22aa0c
--- /dev/null
+++ b/web/src/beta/components/Icon/icons.ts
@@ -0,0 +1,7 @@
+
+// Dataset
+import File from "./Icons/fileIcon.svg";
+
+export default {
+ file: File,
+};
diff --git a/web/src/beta/components/Icon/index.stories.tsx b/web/src/beta/components/Icon/index.stories.tsx
new file mode 100644
index 000000000..372f101b9
--- /dev/null
+++ b/web/src/beta/components/Icon/index.stories.tsx
@@ -0,0 +1,19 @@
+import { Meta } from "@storybook/react";
+
+import Icon from ".";
+
+const icon =
+ '';
+
+export default {
+ title: "Icon",
+ component: Icon,
+} as Meta;
+
+export const Default = () => ;
+export const Color = () => ;
+export const Image = () => ;
+export const Svg = () => ;
+export const Wrapped = () => (
+
+);
diff --git a/web/src/beta/components/Icon/index.tsx b/web/src/beta/components/Icon/index.tsx
new file mode 100644
index 000000000..132045435
--- /dev/null
+++ b/web/src/beta/components/Icon/index.tsx
@@ -0,0 +1,106 @@
+import svgToMiniDataURI from "mini-svg-data-uri";
+import React, { AriaAttributes, AriaRole, CSSProperties, memo, useMemo } from "react";
+import SVG from "react-inlinesvg";
+
+import { ariaProps } from "@reearth/classic/util/aria";
+import { styled } from "@reearth/services/theme";
+
+import Icons from "./icons";
+
+export type Icons = keyof typeof Icons;
+
+export type Props = {
+ className?: string;
+ icon?: string;
+ size?: string | number;
+ alt?: string;
+ color?: string;
+ stroke?: string;
+ style?: CSSProperties;
+ role?: AriaRole;
+ notransition?: boolean;
+ transitionDuration?: string;
+ onClick?: () => void;
+} & AriaAttributes;
+
+const Icon: React.FC = ({
+ className,
+ icon,
+ alt,
+ style,
+ color,
+ stroke,
+ size,
+ role,
+ notransition,
+ transitionDuration,
+ onClick,
+ ...props
+}) => {
+ const src = useMemo(
+ () => (icon?.startsWith("
+ );
+ }
+
+ return (
+
+ );
+};
+
+const StyledImg = styled.img<{ size?: string; notransition?: boolean }>`
+ width: ${({ size }) => size};
+ height: ${({ size }) => size};
+ ${({ notransition }) => !notransition && "transition: all 0.3s;"}
+`;
+
+const StyledSvg = styled(SVG)<{
+ color?: string;
+ stroke?: string;
+ size?: string;
+}>`
+ font-size: 0;
+ display: inline-block;
+ width: ${({ size }) => size};
+ height: ${({ size }) => size};
+ color: ${({ color }) => color};
+ ${({ stroke }) => `stroke: ${stroke};`}
+ transition-property: color, background;
+`;
+
+export default memo(Icon);