diff --git a/demo_markdown/docs/radio/mod.md b/demo_markdown/docs/radio/mod.md
index 6eb9c3b..87d5b84 100644
--- a/demo_markdown/docs/radio/mod.md
+++ b/demo_markdown/docs/radio/mod.md
@@ -8,10 +8,45 @@ view! {
 }
 ```
 
+### Group
+
+```rust demo
+let value = create_rw_signal(None);
+
+view! {
+    <RadioGroup value>
+        <RadioItem key="a">
+            "Apple"
+        </RadioItem>
+        <RadioItem key="o">
+            "Orange"
+        </RadioItem>
+    </RadioGroup>
+    <div style="margin-top: 1rem">
+        "value: " {move || format!("{:?}", value.get())}
+    </div>
+}
+```
+
 ### Radio Props
 
 | Name     | Type                                | Default              | Description                              |
 | -------- | ----------------------------------- | -------------------- | ---------------------------------------- |
 | class    | `OptionalProp<MaybeSignal<String>>` | `Default::default()` | Addtional classes for the radio element. |
 | value    | `Model<bool>`                       | `false`              | Checked value.                           |
-| children | `Children`                          |                      | Radio's content.                         |
+| children | `Option<Children>`                  | `None`               | Radio's content.                         |
+
+### RadioGroup Props
+
+| Name     | Type                    | Default              | Description                        |
+| -------- | ----------------------- | -------------------- | ---------------------------------- |
+| value    | `Model<Option<String>>` | `Default::default()` | Sets the value of the radio group. |
+| children | `Children`              |                      | RadioGroup's content.              |
+
+### RadioItem Props
+
+| Name | Type | Default | Description |
+| --- | --- | --- | --- |
+| class | `OptionalProp<MaybeSignal<String>>` | `Default::default()` | Addtional classes for the radio element. |
+| key | `String` |  | The key of the radio to be used in a radio group. |
+| children | `Option<Children>` | `None` | Radio's content. |
diff --git a/thaw/src/radio/mod.rs b/thaw/src/radio/mod.rs
index 124205e..8ee46fc 100644
--- a/thaw/src/radio/mod.rs
+++ b/thaw/src/radio/mod.rs
@@ -1,3 +1,9 @@
+mod radio_group;
+mod radio_item;
+
+pub use radio_group::RadioGroup;
+pub use radio_item::RadioItem;
+
 use crate::{
     theme::use_theme,
     utils::{class_list::class_list, mount_style, Model, OptionalProp},
@@ -9,7 +15,7 @@ use leptos::*;
 pub fn Radio(
     #[prop(optional, into)] value: Model<bool>,
     #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
-    children: Children,
+    #[prop(optional)] children: Option<Children>,
 ) -> impl IntoView {
     let theme = use_theme(Theme::light);
     mount_style("radio", include_str!("./radio.css"));
@@ -36,7 +42,9 @@ pub fn Radio(
         >
             <input class="thaw-radio__input" type="radio" prop:value=move || value.get()/>
             <div class="thaw-radio__dot"></div>
-            <div class="thaw-radio__label">{children()}</div>
+            <div class="thaw-radio__label">
+                {children.map(|children| children())}
+            </div>
         </div>
     }
 }
diff --git a/thaw/src/radio/radio_group.rs b/thaw/src/radio/radio_group.rs
new file mode 100644
index 0000000..0efd55b
--- /dev/null
+++ b/thaw/src/radio/radio_group.rs
@@ -0,0 +1,17 @@
+use crate::utils::Model;
+use leptos::*;
+
+#[component]
+pub fn RadioGroup(
+    #[prop(optional, into)] value: Model<Option<String>>,
+    children: Children,
+) -> impl IntoView {
+    view! { <Provider value=RadioGroupInjection(value) children/> }
+}
+
+#[derive(Clone)]
+pub(crate) struct RadioGroupInjection(pub Model<Option<String>>);
+
+pub(crate) fn use_radio_group() -> RadioGroupInjection {
+    expect_context()
+}
diff --git a/thaw/src/radio/radio_item.rs b/thaw/src/radio/radio_item.rs
new file mode 100644
index 0000000..0a393e8
--- /dev/null
+++ b/thaw/src/radio/radio_item.rs
@@ -0,0 +1,44 @@
+use crate::{
+    radio::{radio_group::use_radio_group, Radio},
+    utils::OptionalProp,
+};
+use leptos::*;
+
+#[component]
+pub fn RadioItem(
+    #[prop(optional, into)] class: OptionalProp<MaybeSignal<String>>,
+    #[prop(into)] key: String,
+    #[prop(optional)] children: Option<Children>,
+) -> impl IntoView {
+    let radio_group_value = use_radio_group().0;
+    let checked = RwSignal::new(false);
+    let item_key = store_value(key);
+
+    let is_checked = Memo::new(move |_| {
+        radio_group_value.with(|value| item_key.with_value(|key| value.as_ref() == Some(key)))
+    });
+
+    Effect::new(move |prev| {
+        let checked = checked.get();
+        if prev.is_some() {
+            if !is_checked.get_untracked() {
+                radio_group_value.set(Some(item_key.get_value()))
+            }
+        }
+
+        checked
+    });
+
+    if let Some(children) = children {
+        view! {
+            <Radio class value=(is_checked, checked.write_only())>
+                {children()}
+            </Radio>
+        }
+    } else {
+        view! {
+            <Radio class value=(is_checked, checked.write_only())>
+            </Radio>
+        }
+    }
+}