diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml
index b78f820..899c0f2 100644
--- a/.github/workflows/dotnet.yml
+++ b/.github/workflows/dotnet.yml
@@ -23,7 +23,11 @@ jobs:
     - name: Setup .NET
       uses: actions/setup-dotnet@v3
       with:
-        dotnet-version: 9.0.x
+        dotnet-version: |
+          6.0.x
+          7.0.x
+          8.0.x
+          9.0.x
     - name: Restore dependencies
       run: dotnet restore
     - name: Build
diff --git a/InertiaCore/Extensions/InertiaExtensions.cs b/InertiaCore/Extensions/InertiaExtensions.cs
index 9aa0391..90c058d 100644
--- a/InertiaCore/Extensions/InertiaExtensions.cs
+++ b/InertiaCore/Extensions/InertiaExtensions.cs
@@ -8,31 +8,6 @@ namespace InertiaCore.Extensions;
 
 internal static class InertiaExtensions
 {
-    internal static Dictionary<string, object?> OnlyProps(this ActionContext context, Dictionary<string, object?> props)
-    {
-        var onlyKeys = context.HttpContext.Request.Headers[InertiaHeader.PartialOnly]
-            .ToString().Split(',')
-            .Select(k => k.Trim())
-            .Where(k => !string.IsNullOrEmpty(k))
-            .ToList();
-
-        return props.Where(kv => onlyKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase))
-            .ToDictionary(kv => kv.Key, kv => kv.Value);
-    }
-
-    internal static Dictionary<string, object?> ExceptProps(this ActionContext context,
-        Dictionary<string, object?> props)
-    {
-        var exceptKeys = context.HttpContext.Request.Headers[InertiaHeader.PartialExcept]
-            .ToString().Split(',')
-            .Select(k => k.Trim())
-            .Where(k => !string.IsNullOrEmpty(k))
-            .ToList();
-
-        return props.Where(kv => exceptKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase) == false)
-            .ToDictionary(kv => kv.Key, kv => kv.Value);
-    }
-
     internal static bool IsInertiaPartialComponent(this ActionContext context, string component) =>
         context.HttpContext.Request.Headers[InertiaHeader.PartialComponent] == component;
 
@@ -55,4 +30,23 @@ internal static bool Override<TKey, TValue>(this IDictionary<TKey, TValue> dicti
 
         return true;
     }
+
+    internal static Task<object?> ResolveAsync(this Func<object?> func)
+    {
+        var rt = func.Method.ReturnType;
+
+        if (!rt.IsGenericType || rt.GetGenericTypeDefinition() != typeof(Task<>))
+            return Task.Run(func.Invoke);
+
+        var task = func.DynamicInvoke() as Task;
+        return task!.ResolveResult();
+    }
+
+    internal static async Task<object?> ResolveResult(this Task task)
+    {
+        await task.ConfigureAwait(false);
+        var result = task.GetType().GetProperty("Result");
+
+        return result?.GetValue(task);
+    }
 }
diff --git a/InertiaCore/Props/InvokableProp.cs b/InertiaCore/Props/InvokableProp.cs
index 2fd3f3b..02ddb6f 100644
--- a/InertiaCore/Props/InvokableProp.cs
+++ b/InertiaCore/Props/InvokableProp.cs
@@ -1,3 +1,5 @@
+using InertiaCore.Extensions;
+
 namespace InertiaCore.Props;
 
 public class InvokableProp
@@ -10,9 +12,9 @@ public class InvokableProp
     {
         return _value switch
         {
-            Func<Task<object?>> asyncCallable => asyncCallable.Invoke(),
-            Func<object?> callable => Task.Run(() => callable.Invoke()),
-            Task<object?> value => value,
+            Func<object?> f => f.ResolveAsync(),
+            Task t => t.ResolveResult(),
+            InvokableProp p => p.Invoke(),
             _ => Task.FromResult(_value)
         };
     }
diff --git a/InertiaCore/Response.cs b/InertiaCore/Response.cs
index d052844..4b9ed72 100644
--- a/InertiaCore/Response.cs
+++ b/InertiaCore/Response.cs
@@ -13,7 +13,7 @@ namespace InertiaCore;
 public class Response : IActionResult
 {
     private readonly string _component;
-    private readonly object _props;
+    private readonly Dictionary<string, object?> _props;
     private readonly string _rootView;
     private readonly string? _version;
 
@@ -21,46 +21,149 @@ public class Response : IActionResult
     private Page? _page;
     private IDictionary<string, object>? _viewData;
 
-    public Response(string component, object props, string rootView, string? version)
+    internal Response(string component, Dictionary<string, object?> props, string rootView, string? version)
         => (_component, _props, _rootView, _version) = (component, props, rootView, version);
 
     public async Task ExecuteResultAsync(ActionContext context)
     {
         SetContext(context);
         await ProcessResponse();
-
         await GetResult().ExecuteResultAsync(_context!);
     }
 
     protected internal async Task ProcessResponse()
     {
+        var props = await ResolveProperties();
+
         var page = new Page
         {
             Component = _component,
             Version = _version,
             Url = _context!.RequestedUri(),
-            Props = await ResolveProperties(_props.GetType().GetProperties()
-                .ToDictionary(o => o.Name.ToCamelCase(), o => o.GetValue(_props)))
+            Props = props
         };
 
-        var shared = _context!.HttpContext.Features.Get<InertiaSharedData>();
-        if (shared != null)
-            page.Props = shared.GetMerged(page.Props);
-
         page.Props["errors"] = GetErrors();
 
         SetPage(page);
     }
 
-    private static async Task<Dictionary<string, object?>> PrepareProps(Dictionary<string, object?> props)
+    /// <summary>
+    /// Resolve the properties for the response.
+    /// </summary>
+    private async Task<Dictionary<string, object?>> ResolveProperties()
+    {
+        var props = _props;
+
+        props = ResolveSharedProps(props);
+        props = ResolvePartialProperties(props);
+        props = ResolveAlways(props);
+        props = await ResolvePropertyInstances(props);
+
+        return props;
+    }
+
+    /// <summary>
+    /// Resolve `shared` props stored in the current request context.
+    /// </summary>
+    private Dictionary<string, object?> ResolveSharedProps(Dictionary<string, object?> props)
+    {
+        var shared = _context!.HttpContext.Features.Get<InertiaSharedProps>();
+        if (shared != null)
+            props = shared.GetMerged(props);
+
+        return props;
+    }
+
+    /// <summary>
+    /// Resolve the `only` and `except` partial request props.
+    /// </summary>
+    private Dictionary<string, object?> ResolvePartialProperties(Dictionary<string, object?> props)
+    {
+        var isPartial = _context!.IsInertiaPartialComponent(_component);
+
+        if (!isPartial)
+            return props
+                .Where(kv => kv.Value is not LazyProp)
+                .ToDictionary(kv => kv.Key, kv => kv.Value);
+
+        props = props.ToDictionary(kv => kv.Key, kv => kv.Value);
+
+        if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialOnly))
+            props = ResolveOnly(props);
+
+        if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialExcept))
+            props = ResolveExcept(props);
+
+        return props;
+    }
+
+    /// <summary>
+    /// Resolve the `only` partial request props.
+    /// </summary>
+    private Dictionary<string, object?> ResolveOnly(Dictionary<string, object?> props)
+    {
+        var onlyKeys = _context!.HttpContext.Request.Headers[InertiaHeader.PartialOnly]
+            .ToString().Split(',')
+            .Select(k => k.Trim())
+            .Where(k => !string.IsNullOrEmpty(k))
+            .ToList();
+
+        return props.Where(kv => onlyKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase))
+            .ToDictionary(kv => kv.Key, kv => kv.Value);
+    }
+
+    /// <summary>
+    /// Resolve the `except` partial request props.
+    /// </summary>
+    private Dictionary<string, object?> ResolveExcept(Dictionary<string, object?> props)
+    {
+        var exceptKeys = _context!.HttpContext.Request.Headers[InertiaHeader.PartialExcept]
+            .ToString().Split(',')
+            .Select(k => k.Trim())
+            .Where(k => !string.IsNullOrEmpty(k))
+            .ToList();
+
+        return props.Where(kv => exceptKeys.Contains(kv.Key, StringComparer.OrdinalIgnoreCase) == false)
+            .ToDictionary(kv => kv.Key, kv => kv.Value);
+    }
+
+    /// <summary>
+    /// Resolve `always` properties that should always be included on all visits, regardless of "only" or "except" requests.
+    /// </summary>
+    private Dictionary<string, object?> ResolveAlways(Dictionary<string, object?> props)
+    {
+        var alwaysProps = _props.Where(o => o.Value is AlwaysProp);
+
+        return props
+            .Where(kv => kv.Value is not AlwaysProp)
+            .Concat(alwaysProps).ToDictionary(kv => kv.Key, kv => kv.Value);
+    }
+
+    /// <summary>
+    /// Resolve all necessary class instances in the given props.
+    /// </summary>
+    private static async Task<Dictionary<string, object?>> ResolvePropertyInstances(Dictionary<string, object?> props)
     {
-        return (await Task.WhenAll(props.Select(async pair => pair.Value switch
+        return (await Task.WhenAll(props.Select(async pair =>
         {
-            Func<object?> f => (pair.Key, f.Invoke()),
-            LazyProp l => (pair.Key, await l.Invoke()),
-            AlwaysProp l => (pair.Key, await l.Invoke()),
-            _ => (pair.Key, pair.Value)
-        }))).ToDictionary(pair => pair.Key, pair => pair.Item2);
+            var key = pair.Key.ToCamelCase();
+
+            var value = pair.Value switch
+            {
+                Func<object?> f => (key, await f.ResolveAsync()),
+                Task t => (key, await t.ResolveResult()),
+                InvokableProp p => (key, await p.Invoke()),
+                _ => (key, pair.Value)
+            };
+
+            if (value.Item2 is Dictionary<string, object?> dict)
+            {
+                value = (key, await ResolvePropertyInstances(dict));
+            }
+
+            return value;
+        }))).ToDictionary(pair => pair.key, pair => pair.Item2);
     }
 
     protected internal JsonResult GetJson()
@@ -93,7 +196,7 @@ private ViewResult GetView()
 
     protected internal IActionResult GetResult() => _context!.IsInertiaRequest() ? GetJson() : GetView();
 
-    private IDictionary<string, string> GetErrors()
+    private Dictionary<string, string> GetErrors()
     {
         if (!_context!.ModelState.IsValid)
             return _context!.ModelState.ToDictionary(o => o.Key.ToCamelCase(),
@@ -111,48 +214,4 @@ public Response WithViewData(IDictionary<string, object> viewData)
         _viewData = viewData;
         return this;
     }
-
-    private async Task<Dictionary<string, object?>> ResolveProperties(Dictionary<string, object?> props)
-    {
-        var isPartial = _context!.IsInertiaPartialComponent(_component);
-
-        if (!isPartial)
-        {
-            props = props
-                .Where(kv => kv.Value is not LazyProp)
-                .ToDictionary(kv => kv.Key, kv => kv.Value);
-        }
-        else
-        {
-            props = props.ToDictionary(kv => kv.Key, kv => kv.Value);
-
-            if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialOnly))
-                props = ResolveOnly(props);
-
-            if (_context!.HttpContext.Request.Headers.ContainsKey(InertiaHeader.PartialExcept))
-                props = ResolveExcept(props);
-        }
-
-        props = ResolveAlways(props);
-        props = await PrepareProps(props);
-
-        return props;
-    }
-
-    private Dictionary<string, object?> ResolveOnly(Dictionary<string, object?> props)
-        => _context!.OnlyProps(props);
-
-    private Dictionary<string, object?> ResolveExcept(Dictionary<string, object?> props)
-        => _context!.ExceptProps(props);
-
-    private Dictionary<string, object?> ResolveAlways(Dictionary<string, object?> props)
-    {
-        var alwaysProps = _props.GetType().GetProperties()
-            .Where(o => o.PropertyType == typeof(AlwaysProp))
-            .ToDictionary(o => o.Name.ToCamelCase(), o => o.GetValue(_props));
-
-        return props
-            .Where(kv => kv.Value is not AlwaysProp)
-            .Concat(alwaysProps).ToDictionary(kv => kv.Key, kv => kv.Value);
-    }
 }
diff --git a/InertiaCore/ResponseFactory.cs b/InertiaCore/ResponseFactory.cs
index b5fbd21..8bce7cf 100644
--- a/InertiaCore/ResponseFactory.cs
+++ b/InertiaCore/ResponseFactory.cs
@@ -42,8 +42,14 @@ public ResponseFactory(IHttpContextAccessor contextAccessor, IGateway gateway, I
     public Response Render(string component, object? props = null)
     {
         props ??= new { };
+        var dictProps = props switch
+        {
+            Dictionary<string, object?> dict => dict,
+            _ => props.GetType().GetProperties()
+                .ToDictionary(o => o.Name, o => o.GetValue(props))
+        };
 
-        return new Response(component, props, _options.Value.RootView, GetVersion());
+        return new Response(component, dictProps, _options.Value.RootView, GetVersion());
     }
 
     public async Task<IHtmlContent> Head(dynamic model)
@@ -104,8 +110,8 @@ public void Share(string key, object? value)
     {
         var context = _contextAccessor.HttpContext!;
 
-        var sharedData = context.Features.Get<InertiaSharedData>();
-        sharedData ??= new InertiaSharedData();
+        var sharedData = context.Features.Get<InertiaSharedProps>();
+        sharedData ??= new InertiaSharedProps();
         sharedData.Set(key, value);
 
         context.Features.Set(sharedData);
@@ -115,16 +121,16 @@ public void Share(IDictionary<string, object?> data)
     {
         var context = _contextAccessor.HttpContext!;
 
-        var sharedData = context.Features.Get<InertiaSharedData>();
-        sharedData ??= new InertiaSharedData();
+        var sharedData = context.Features.Get<InertiaSharedProps>();
+        sharedData ??= new InertiaSharedProps();
         sharedData.Merge(data);
 
         context.Features.Set(sharedData);
     }
 
-    public LazyProp Lazy(Func<object?> callback) => new LazyProp(callback);
-    public LazyProp Lazy(Func<Task<object?>> callback) => new LazyProp(callback);
-    public AlwaysProp Always(object? value) => new AlwaysProp(value);
-    public AlwaysProp Always(Func<object?> callback) => new AlwaysProp(callback);
-    public AlwaysProp Always(Func<Task<object?>> callback) => new AlwaysProp(callback);
+    public LazyProp Lazy(Func<object?> callback) => new(callback);
+    public LazyProp Lazy(Func<Task<object?>> callback) => new(callback);
+    public AlwaysProp Always(object? value) => new(value);
+    public AlwaysProp Always(Func<object?> callback) => new(callback);
+    public AlwaysProp Always(Func<Task<object?>> callback) => new(callback);
 }
diff --git a/InertiaCore/Utils/InertiaSharedData.cs b/InertiaCore/Utils/InertiaSharedProps.cs
similarity index 95%
rename from InertiaCore/Utils/InertiaSharedData.cs
rename to InertiaCore/Utils/InertiaSharedProps.cs
index dfb9ff7..68b9bbd 100644
--- a/InertiaCore/Utils/InertiaSharedData.cs
+++ b/InertiaCore/Utils/InertiaSharedProps.cs
@@ -2,7 +2,7 @@
 
 namespace InertiaCore.Utils;
 
-internal class InertiaSharedData
+internal class InertiaSharedProps
 {
     private IDictionary<string, object?>? Data { get; set; }
 
diff --git a/InertiaCoreTests/Setup.cs b/InertiaCoreTests/Setup.cs
index bb70e88..5942c2b 100644
--- a/InertiaCoreTests/Setup.cs
+++ b/InertiaCoreTests/Setup.cs
@@ -33,9 +33,9 @@ public void Setup()
     ///     Prepares ActionContext for usage in tests.
     /// </summary>
     /// <param name="headers">Optional request headers.</param>
-    /// <param name="sharedData">Optional Inertia shared data.</param>
+    /// <param name="sharedProps">Optional Inertia shared data.</param>
     /// <param name="modelState">Optional validation errors dictionary.</param>
-    private static ActionContext PrepareContext(HeaderDictionary? headers = null, InertiaSharedData? sharedData = null,
+    private static ActionContext PrepareContext(HeaderDictionary? headers = null, InertiaSharedProps? sharedProps = null,
         Dictionary<string, string>? modelState = null)
     {
         var request = new Mock<HttpRequest>();
@@ -45,8 +45,8 @@ private static ActionContext PrepareContext(HeaderDictionary? headers = null, In
         response.SetupGet(r => r.Headers).Returns(new HeaderDictionary());
 
         var features = new FeatureCollection();
-        if (sharedData != null)
-            features.Set(sharedData);
+        if (sharedProps != null)
+            features.Set(sharedProps);
 
         var httpContext = new Mock<HttpContext>();
         httpContext.SetupGet(c => c.Request).Returns(request.Object);
diff --git a/InertiaCoreTests/UnitTestAlwaysData.cs b/InertiaCoreTests/UnitTestAlwaysData.cs
index 9374fb0..480ac47 100644
--- a/InertiaCoreTests/UnitTestAlwaysData.cs
+++ b/InertiaCoreTests/UnitTestAlwaysData.cs
@@ -67,17 +67,11 @@ public async Task TestAlwaysPartialData()
     [Description("Test if the always async data is fetched properly.")]
     public async Task TestAlwaysAsyncData()
     {
-        var testFunction = new Func<Task<object?>>(async () =>
-        {
-            await Task.Delay(100);
-            return "Always Async";
-        });
-
         var response = _factory.Render("Test/Page", new
         {
             Test = "Test",
             TestFunc = new Func<string>(() => "Func"),
-            TestAlways = _factory.Always(testFunction)
+            TestAlways = _factory.Always(() => Task.FromResult<object?>("Always Async"))
         });
 
         var context = PrepareContext();
@@ -100,16 +94,10 @@ public async Task TestAlwaysAsyncData()
     [Description("Test if the always async data is fetched properly with specified partial props.")]
     public async Task TestAlwaysAsyncPartialData()
     {
-        var testFunction = new Func<Task<string>>(async () =>
-        {
-            await Task.Delay(100);
-            return "Always Async";
-        });
-
         var response = _factory.Render("Test/Page", new
         {
             TestFunc = new Func<string>(() => "Func"),
-            TestAlways = _factory.Always(async () => await testFunction())
+            TestAlways = _factory.Always(() => Task.FromResult<object?>("Always Async"))
         });
 
         var headers = new HeaderDictionary
@@ -137,16 +125,10 @@ public async Task TestAlwaysAsyncPartialData()
     [Description("Test if the always async data is fetched properly without specified partial props.")]
     public async Task TestAlwaysAsyncPartialDataOmitted()
     {
-        var testFunction = new Func<Task<string>>(async () =>
-        {
-            await Task.Delay(100);
-            return "Always Async";
-        });
-
         var response = _factory.Render("Test/Page", new
         {
             TestFunc = new Func<string>(() => "Func"),
-            TestAlways = _factory.Always(async () => await testFunction())
+            TestAlways = _factory.Always(() => Task.FromResult<object?>("Always Async"))
         });
 
         var headers = new HeaderDictionary
diff --git a/InertiaCoreTests/UnitTestDictionaryData.cs b/InertiaCoreTests/UnitTestDictionaryData.cs
new file mode 100644
index 0000000..cc3d538
--- /dev/null
+++ b/InertiaCoreTests/UnitTestDictionaryData.cs
@@ -0,0 +1,53 @@
+using InertiaCore.Models;
+
+namespace InertiaCoreTests;
+
+public partial class Tests
+{
+    [Test]
+    [Description("Test if all nested dictionaries and its values are resolved properly.")]
+    public async Task TestDictionaryData()
+    {
+        var response = _factory.Render("Test/Page", new
+        {
+            Test = "Test",
+            TestDict = new Dictionary<string, object>
+            {
+                ["Key"] = () => "Value",
+                ["KeyAsync"] = () => Task.FromResult("ValueAsync"),
+                ["KeyAsync2"] = Task.FromResult("ValueAsync2"),
+                ["Nested"] = () => new Dictionary<string, object>
+                {
+                    ["Key"] = () => "Value"
+                }
+            }
+        });
+
+        var context = PrepareContext();
+        response.SetContext(context);
+
+        await response.ProcessResponse();
+
+        var page = response.GetJson().Value as Page;
+
+        Assert.That(page?.Props, Is.EqualTo(new Dictionary<string, object?>
+        {
+            { "test", "Test" },
+            {
+                "testDict", new Dictionary<string, object?>
+                {
+                    { "key", "Value" },
+                    { "keyAsync", "ValueAsync" },
+                    { "keyAsync2", "ValueAsync2" },
+                    {
+                        "nested", new Dictionary<string, object?>
+                        {
+                            { "key", "Value" },
+                        }
+                    }
+                }
+            },
+            { "errors", new Dictionary<string, string>(0) }
+        }));
+    }
+}
diff --git a/InertiaCoreTests/UnitTestLazyData.cs b/InertiaCoreTests/UnitTestLazyData.cs
index 423cbf4..085eb60 100644
--- a/InertiaCoreTests/UnitTestLazyData.cs
+++ b/InertiaCoreTests/UnitTestLazyData.cs
@@ -71,18 +71,15 @@ public async Task TestLazyPartialData()
     [Description("Test if the lazy async data is fetched properly.")]
     public async Task TestLazyAsyncData()
     {
-        var testFunction = new Func<Task<object?>>(async () =>
-        {
-            Assert.Fail();
-            await Task.Delay(100);
-            return "Lazy Async";
-        });
-
         var response = _factory.Render("Test/Page", new
         {
             Test = "Test",
             TestFunc = new Func<string>(() => "Func"),
-            TestLazy = _factory.Lazy(testFunction)
+            TestLazy = _factory.Lazy(() =>
+            {
+                Assert.Fail();
+                return Task.FromResult<object?>("Lazy Async");
+            })
         });
 
         var context = PrepareContext();
@@ -104,16 +101,10 @@ public async Task TestLazyAsyncData()
     [Description("Test if the lazy async data is fetched properly with specified partial props.")]
     public async Task TestLazyAsyncPartialData()
     {
-        var testFunction = new Func<Task<string>>(async () =>
-        {
-            await Task.Delay(100);
-            return "Lazy Async";
-        });
-
         var response = _factory.Render("Test/Page", new
         {
             TestFunc = new Func<string>(() => "Func"),
-            TestLazy = _factory.Lazy(async () => await testFunction())
+            TestLazy = _factory.Lazy(() => Task.FromResult<object?>("Lazy Async"))
         });
 
         var headers = new HeaderDictionary
diff --git a/InertiaCoreTests/UnitTestSharedData.cs b/InertiaCoreTests/UnitTestSharedData.cs
index 9155c8a..fc64336 100644
--- a/InertiaCoreTests/UnitTestSharedData.cs
+++ b/InertiaCoreTests/UnitTestSharedData.cs
@@ -7,17 +7,17 @@ public partial class Tests
 {
     [Test]
     [Description("Test if shared data is merged with the props properly.")]
-    public async Task TestSharedData()
+    public async Task TestSharedProps()
     {
         var response = _factory.Render("Test/Page", new
         {
             Test = "Test"
         });
 
-        var sharedData = new InertiaSharedData();
-        sharedData.Set("TestShared", "Shared");
+        var sharedProps = new InertiaSharedProps();
+        sharedProps.Set("TestShared", "Shared");
 
-        var context = PrepareContext(null, sharedData);
+        var context = PrepareContext(null, sharedProps);
 
         response.SetContext(context);
         await response.ProcessResponse();